From a6444a69e27275c69e7287fe02ba0c88d554c445 Mon Sep 17 00:00:00 2001 From: Nahua Kang Date: Tue, 23 Aug 2022 19:05:57 +0200 Subject: [PATCH 001/169] Remove if_chain from equatable_if_let --- clippy_lints/src/equatable_if_let.rs | 59 +++++++++++++--------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index fdfb821ac7895..ba615c8c16484 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -67,37 +66,33 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T impl<'tcx> LateLintPass<'tcx> for PatternEquality { 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::Let(let_expr) = expr.kind; - if unary_pattern(let_expr.pat); - let exp_ty = cx.typeck_results().expr_ty(let_expr.init); - let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); - if is_structural_partial_eq(cx, exp_ty, pat_ty); - then { - - let mut applicability = Applicability::MachineApplicable; - let pat_str = match let_expr.pat.kind { - PatKind::Struct(..) => format!( - "({})", - snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, - ), - _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), - }; - span_lint_and_sugg( - cx, - EQUATABLE_IF_LET, - expr.span, - "this pattern matching can be expressed using equality", - "try", - format!( - "{} == {}", - snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, - pat_str, - ), - applicability, - ); - } + if !in_external_macro(cx.sess(), expr.span) + && let ExprKind::Let(let_expr) = expr.kind + && unary_pattern(let_expr.pat) + && let exp_ty = cx.typeck_results().expr_ty(let_expr.init) + && let pat_ty = cx.typeck_results().pat_ty(let_expr.pat) + && is_structural_partial_eq(cx, exp_ty, pat_ty) { + let mut applicability = Applicability::MachineApplicable; + let pat_str = match let_expr.pat.kind { + PatKind::Struct(..) => format!( + "({})", + snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, + ), + _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), + }; + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using equality", + "try", + format!( + "{} == {}", + snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, + pat_str, + ), + applicability, + ); } } } From 5ee1c24f28029162ad25614c092224cc94ba59ff Mon Sep 17 00:00:00 2001 From: Nahua Kang Date: Tue, 23 Aug 2022 19:46:04 +0200 Subject: [PATCH 002/169] Lint suggests matches macro if PartialEq trait is not implemented --- clippy_lints/src/equatable_if_let.rs | 64 +++++++++++++++++----------- tests/ui/equatable_if_let.rs | 7 +++ 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index ba615c8c16484..6f26195d105ce 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -68,31 +68,47 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if !in_external_macro(cx.sess(), expr.span) && let ExprKind::Let(let_expr) = expr.kind - && unary_pattern(let_expr.pat) - && let exp_ty = cx.typeck_results().expr_ty(let_expr.init) - && let pat_ty = cx.typeck_results().pat_ty(let_expr.pat) - && is_structural_partial_eq(cx, exp_ty, pat_ty) { + && unary_pattern(let_expr.pat) { + let exp_ty = cx.typeck_results().expr_ty(let_expr.init); + let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); let mut applicability = Applicability::MachineApplicable; - let pat_str = match let_expr.pat.kind { - PatKind::Struct(..) => format!( - "({})", - snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, - ), - _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), - }; - span_lint_and_sugg( - cx, - EQUATABLE_IF_LET, - expr.span, - "this pattern matching can be expressed using equality", - "try", - format!( - "{} == {}", - snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, - pat_str, - ), - applicability, - ); + + if is_structural_partial_eq(cx, exp_ty, pat_ty) { + let pat_str = match let_expr.pat.kind { + PatKind::Struct(..) => format!( + "({})", + snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, + ), + _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), + }; + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using equality", + "try", + format!( + "{} == {}", + snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, + pat_str, + ), + applicability, + ); + } else { + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using `matches!`", + "try", + format!( + "matches!({}, {})", + snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, + snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, + ), + applicability, + ) + } } } } diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index 8c467d14d2a9f..c3626c081dd5e 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -23,6 +23,11 @@ struct Struct { b: bool, } +struct NoPartialEqStruct { + a: i32, + b: bool, +} + enum NotPartialEq { A, B, @@ -47,6 +52,7 @@ fn main() { let e = Enum::UnitVariant; let f = NotPartialEq::A; let g = NotStructuralEq::A; + let h = NoPartialEqStruct { a: 2, b: false }; // true @@ -70,6 +76,7 @@ fn main() { if let NotStructuralEq::A = g {} if let Some(NotPartialEq::A) = Some(f) {} if let Some(NotStructuralEq::A) = Some(g) {} + if let NoPartialEqStruct { a: 2, b: false } = h {} macro_rules! m1 { (x) => { From 4eaadd622d21948c488a4952b3c65fd5b9d852dc Mon Sep 17 00:00:00 2001 From: Nahua Kang Date: Tue, 23 Aug 2022 19:50:34 +0200 Subject: [PATCH 003/169] Run cargo dev bless to update fixes & stderr --- clippy_lints/src/equatable_if_let.rs | 10 +++---- tests/ui/equatable_if_let.fixed | 11 ++++++-- tests/ui/equatable_if_let.stderr | 42 ++++++++++++++++++++-------- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index 6f26195d105ce..928128785f49a 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -96,18 +96,18 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { ); } else { span_lint_and_sugg( - cx, + cx, EQUATABLE_IF_LET, expr.span, - "this pattern matching can be expressed using `matches!`", - "try", + "this pattern matching can be expressed using `matches!`", + "try", format!( "matches!({}, {})", snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, - ), + ), applicability, - ) + ); } } } diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 687efdada6e31..9af2ba9627200 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -23,6 +23,11 @@ struct Struct { b: bool, } +struct NoPartialEqStruct { + a: i32, + b: bool, +} + enum NotPartialEq { A, B, @@ -47,6 +52,7 @@ fn main() { let e = Enum::UnitVariant; let f = NotPartialEq::A; let g = NotStructuralEq::A; + let h = NoPartialEqStruct { a: 2, b: false }; // true @@ -66,10 +72,11 @@ fn main() { if let Some(3 | 4) = c {} if let Struct { a, b: false } = d {} if let Struct { a: 2, b: x } = d {} - if let NotPartialEq::A = f {} + if matches!(f, NotPartialEq::A) {} if g == NotStructuralEq::A {} - if let Some(NotPartialEq::A) = Some(f) {} + if matches!(Some(f), Some(NotPartialEq::A)) {} if Some(g) == Some(NotStructuralEq::A) {} + if matches!(h, NoPartialEqStruct { a: 2, b: false }) {} macro_rules! m1 { (x) => { diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr index 9c4c3cc3682e6..40ca75b8da22c 100644 --- a/tests/ui/equatable_if_let.stderr +++ b/tests/ui/equatable_if_let.stderr @@ -1,5 +1,5 @@ error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:53:8 + --> $DIR/equatable_if_let.rs:59:8 | LL | if let 2 = a {} | ^^^^^^^^^ help: try: `a == 2` @@ -7,64 +7,82 @@ LL | if let 2 = a {} = note: `-D clippy::equatable-if-let` implied by `-D warnings` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:54:8 + --> $DIR/equatable_if_let.rs:60:8 | LL | if let Ordering::Greater = a.cmp(&b) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:55:8 + --> $DIR/equatable_if_let.rs:61:8 | LL | if let Some(2) = c {} | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:56:8 + --> $DIR/equatable_if_let.rs:62:8 | LL | if let Struct { a: 2, b: false } = d {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:57:8 + --> $DIR/equatable_if_let.rs:63:8 | LL | if let Enum::TupleVariant(32, 64) = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:58:8 + --> $DIR/equatable_if_let.rs:64:8 | LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:59:8 + --> $DIR/equatable_if_let.rs:65:8 | LL | if let Enum::UnitVariant = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:60:8 + --> $DIR/equatable_if_let.rs:66:8 | LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })` +error: this pattern matching can be expressed using `matches!` + --> $DIR/equatable_if_let.rs:75:8 + | +LL | if let NotPartialEq::A = f {} + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)` + error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:70:8 + --> $DIR/equatable_if_let.rs:76:8 | LL | if let NotStructuralEq::A = g {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A` +error: this pattern matching can be expressed using `matches!` + --> $DIR/equatable_if_let.rs:77:8 + | +LL | if let Some(NotPartialEq::A) = Some(f) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(f), Some(NotPartialEq::A))` + error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:72:8 + --> $DIR/equatable_if_let.rs:78:8 | LL | if let Some(NotStructuralEq::A) = Some(g) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)` -error: this pattern matching can be expressed using equality +error: this pattern matching can be expressed using `matches!` --> $DIR/equatable_if_let.rs:79:8 | +LL | if let NoPartialEqStruct { a: 2, b: false } = h {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(h, NoPartialEqStruct { a: 2, b: false })` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:86:8 + | LL | if let m1!(x) = "abc" { | ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)` -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors From 9ebd6916125e9181d1ce340a159e9403afc99188 Mon Sep 17 00:00:00 2001 From: Cody Date: Mon, 10 Oct 2022 23:37:09 -0500 Subject: [PATCH 004/169] Fix allow_attributes_without_reason applying to external crate macros Previously the `clippy::allow_attributes_without_reason` lint would apply to external crate macros. Many macros in the Rust ecosystem include these `allow` attributes without adding a reason, making this lint pretty much unusable in any sizable Rust project. This commit fixes that by adding a check to the lint if the attribute is from an external crate macro and returning early. --- clippy_lints/src/attrs.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 0bd1f8b784e8f..bed9cf565f30b 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -464,6 +464,11 @@ fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem return; } + // Check if the attribute is in an external macro and therefore out of the developer's control + if in_external_macro(cx.sess(), attr.span) { + return; + } + span_lint_and_help( cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, From 1450710f12f74ce6bd55c44e4170fbd352846f3d Mon Sep 17 00:00:00 2001 From: alex-semenyuk Date: Sat, 15 Oct 2022 23:52:40 +0300 Subject: [PATCH 005/169] Enable test no_std_main_recursion --- tests/ui/crate_level_checks/no_std_main_recursion.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ui/crate_level_checks/no_std_main_recursion.rs b/tests/ui/crate_level_checks/no_std_main_recursion.rs index 4a5c597dda51f..e1c9fe30a9dfb 100644 --- a/tests/ui/crate_level_checks/no_std_main_recursion.rs +++ b/tests/ui/crate_level_checks/no_std_main_recursion.rs @@ -1,6 +1,5 @@ // compile-flags: -Clink-arg=-nostartfiles // ignore-macos -// ignore-windows #![feature(lang_items, start, libc)] #![no_std] From 831b99436c3dc47eafdaf363acd1d4e9e230551d Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Wed, 19 Oct 2022 00:08:20 +0200 Subject: [PATCH 006/169] Implement -Ztrack-diagnostics --- clippy_lints/src/doc.rs | 1 + src/driver.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 36dc7e3396b82..9e2facf0f63b7 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -691,6 +691,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { false, None, false, + false, ); let handler = Handler::with_emitter(false, None, Box::new(emitter)); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/src/driver.rs b/src/driver.rs index b12208ac62a88..ae54b2078a65b 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -179,6 +179,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { false, None, false, + false, )); let handler = rustc_errors::Handler::with_emitter(true, None, emitter); From b78509e2f29c12594cb8c9633ef89383f1536e5a Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 19 Oct 2022 11:46:26 +0800 Subject: [PATCH 007/169] Add testcase for next_point, fix more trivial issues in find_width_of_character_at_span --- clippy_utils/src/sugg.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3c5dd92b9cd6d..3347342e412b6 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -769,8 +769,7 @@ impl DiagnosticExt for rustc_errors::Diagnostic { fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) { let mut remove_span = item; - let hi = cx.sess().source_map().next_point(remove_span).hi(); - let fmpos = cx.sess().source_map().lookup_byte_offset(hi); + let fmpos = cx.sess().source_map().lookup_byte_offset(remove_span.hi()); if let Some(ref src) = fmpos.sf.src { let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n'); From 4dfc7b20250b040b34953ccf71b1bbef77a75f7d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 19 Oct 2022 11:34:00 -0700 Subject: [PATCH 008/169] Fixup a few tests needing asm support --- tests/ui/entry.fixed | 1 + tests/ui/entry.rs | 1 + tests/ui/entry.stderr | 20 ++++++++-------- tests/ui/missing_doc.rs | 1 + tests/ui/missing_doc.stderr | 48 ++++++++++++++++++------------------- 5 files changed, 37 insertions(+), 34 deletions(-) diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index e43635abcd112..79c29c04e0596 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -1,3 +1,4 @@ +// needs-asm-support // run-rustfix #![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index d999b3b7dc80d..2d7985457d8b4 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -1,3 +1,4 @@ +// needs-asm-support // run-rustfix #![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index 2ef9966525cef..2c4c49d2522ca 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:24:5 + --> $DIR/entry.rs:25: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:29:5 + --> $DIR/entry.rs:30:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -32,7 +32,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:38:5 + --> $DIR/entry.rs:39:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -55,7 +55,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:47:5 + --> $DIR/entry.rs:48:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -79,7 +79,7 @@ LL + } | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:57:5 + --> $DIR/entry.rs:58:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -96,7 +96,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:63:5 + --> $DIR/entry.rs:64:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -122,7 +122,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:75:5 + --> $DIR/entry.rs:76:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -146,7 +146,7 @@ LL + } | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:85:5 + --> $DIR/entry.rs:86:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -187,7 +187,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:119:5 + --> $DIR/entry.rs:120:5 | LL | / if !m.contains_key(&m!(k)) { LL | | m.insert(m!(k), m!(v)); @@ -195,7 +195,7 @@ LL | | } | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));` error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:151:5 + --> $DIR/entry.rs:152:5 | LL | / if !m.contains_key(&k) { LL | | let x = (String::new(), String::new()); diff --git a/tests/ui/missing_doc.rs b/tests/ui/missing_doc.rs index 29cc026a8fd39..590ad63c90be3 100644 --- a/tests/ui/missing_doc.rs +++ b/tests/ui/missing_doc.rs @@ -1,3 +1,4 @@ +// needs-asm-support // aux-build: proc_macro_with_span.rs #![warn(clippy::missing_docs_in_private_items)] diff --git a/tests/ui/missing_doc.stderr b/tests/ui/missing_doc.stderr index 6c8e66f464377..d3bef28bf64c6 100644 --- a/tests/ui/missing_doc.stderr +++ b/tests/ui/missing_doc.stderr @@ -1,5 +1,5 @@ error: missing documentation for a type alias - --> $DIR/missing_doc.rs:15:1 + --> $DIR/missing_doc.rs:16:1 | LL | type Typedef = String; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -7,37 +7,37 @@ LL | type Typedef = String; = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` error: missing documentation for a type alias - --> $DIR/missing_doc.rs:16:1 + --> $DIR/missing_doc.rs:17:1 | LL | pub type PubTypedef = String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/missing_doc.rs:18:1 + --> $DIR/missing_doc.rs:19:1 | LL | mod module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/missing_doc.rs:19:1 + --> $DIR/missing_doc.rs:20:1 | LL | pub mod pub_module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:23:1 + --> $DIR/missing_doc.rs:24:1 | LL | pub fn foo2() {} | ^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:24:1 + --> $DIR/missing_doc.rs:25:1 | LL | fn foo3() {} | ^^^^^^^^^^^^ error: missing documentation for an enum - --> $DIR/missing_doc.rs:38:1 + --> $DIR/missing_doc.rs:39:1 | LL | / enum Baz { LL | | BazA { a: isize, b: isize }, @@ -46,31 +46,31 @@ LL | | } | |_^ error: missing documentation for a variant - --> $DIR/missing_doc.rs:39:5 + --> $DIR/missing_doc.rs:40:5 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> $DIR/missing_doc.rs:39:12 + --> $DIR/missing_doc.rs:40:12 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^ error: missing documentation for a struct field - --> $DIR/missing_doc.rs:39:22 + --> $DIR/missing_doc.rs:40:22 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^ error: missing documentation for a variant - --> $DIR/missing_doc.rs:40:5 + --> $DIR/missing_doc.rs:41:5 | LL | BarB, | ^^^^ error: missing documentation for an enum - --> $DIR/missing_doc.rs:43:1 + --> $DIR/missing_doc.rs:44:1 | LL | / pub enum PubBaz { LL | | PubBazA { a: isize }, @@ -78,43 +78,43 @@ LL | | } | |_^ error: missing documentation for a variant - --> $DIR/missing_doc.rs:44:5 + --> $DIR/missing_doc.rs:45:5 | LL | PubBazA { a: isize }, | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> $DIR/missing_doc.rs:44:15 + --> $DIR/missing_doc.rs:45:15 | LL | PubBazA { a: isize }, | ^^^^^^^^ error: missing documentation for a constant - --> $DIR/missing_doc.rs:64:1 + --> $DIR/missing_doc.rs:65:1 | LL | const FOO: u32 = 0; | ^^^^^^^^^^^^^^^^^^^ error: missing documentation for a constant - --> $DIR/missing_doc.rs:71:1 + --> $DIR/missing_doc.rs:72:1 | LL | pub const FOO4: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> $DIR/missing_doc.rs:73:1 + --> $DIR/missing_doc.rs:74:1 | LL | static BAR: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> $DIR/missing_doc.rs:80:1 + --> $DIR/missing_doc.rs:81:1 | LL | pub static BAR4: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/missing_doc.rs:82:1 + --> $DIR/missing_doc.rs:83:1 | LL | / mod internal_impl { LL | | /// dox @@ -126,31 +126,31 @@ LL | | } | |_^ error: missing documentation for a function - --> $DIR/missing_doc.rs:85:5 + --> $DIR/missing_doc.rs:86:5 | LL | pub fn undocumented1() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:86:5 + --> $DIR/missing_doc.rs:87:5 | LL | pub fn undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:87:5 + --> $DIR/missing_doc.rs:88:5 | LL | fn undocumented3() {} | ^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:92:9 + --> $DIR/missing_doc.rs:93:9 | LL | pub fn also_undocumented1() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:93:9 + --> $DIR/missing_doc.rs:94:9 | LL | fn also_undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ From 56506730c827e02a69cb803fcaa7e912cf25826b Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Tue, 11 Oct 2022 16:17:59 +0000 Subject: [PATCH 009/169] Implement assertions and fixes to not emit empty spans without suggestions --- clippy_lints/src/manual_assert.rs | 12 ++--- clippy_lints/src/needless_late_init.rs | 11 +++-- tests/ui/manual_assert.edition2018.stderr | 55 ++++------------------- tests/ui/manual_assert.edition2021.stderr | 55 ++++------------------- 4 files changed, 30 insertions(+), 103 deletions(-) diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 825ec84b4a812..b8ed9b9ec18f7 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -69,11 +69,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { "only a `panic!` in `if`-then statement", |diag| { // comments can be noisy, do not show them to the user - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability); + if !comments.is_empty() { + diag.tool_only_span_suggestion( + expr.span.shrink_to_lo(), + "add comments back", + comments, + applicability); + } diag.span_suggestion( expr.span, "try instead", diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 9d26e5900866c..67debe7e08af6 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -180,10 +180,13 @@ fn assignment_suggestions<'tcx>( let suggestions = assignments .iter() .flat_map(|assignment| { - [ - assignment.span.until(assignment.rhs_span), - assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()), - ] + let mut spans = vec![assignment.span.until(assignment.rhs_span)]; + + if assignment.rhs_span.hi() != assignment.span.hi() { + spans.push(assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi())); + } + + spans }) .map(|span| (span, String::new())) .collect::>(); diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 7718588fdf6f4..237638ee1344c 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -4,13 +4,9 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ + | |_____^ help: try instead: `assert!(a.is_empty(), "qaqaq{:?}", a);` | = note: `-D clippy::manual-assert` implied by `-D warnings` -help: try instead - | -LL | assert!(a.is_empty(), "qaqaq{:?}", a); - | error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:34:5 @@ -18,12 +14,7 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(a.is_empty(), "qwqwq"); - | + | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:51:5 @@ -31,12 +22,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!b.is_empty(), "panic1"); - | + | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:54:5 @@ -44,12 +30,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:57:5 @@ -57,12 +38,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:60:5 @@ -70,12 +46,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:63:5 @@ -83,12 +54,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 @@ -96,12 +62,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!a.is_empty(), "with expansion {}", one!()); - | + | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:73:5 diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 7718588fdf6f4..237638ee1344c 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -4,13 +4,9 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ + | |_____^ help: try instead: `assert!(a.is_empty(), "qaqaq{:?}", a);` | = note: `-D clippy::manual-assert` implied by `-D warnings` -help: try instead - | -LL | assert!(a.is_empty(), "qaqaq{:?}", a); - | error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:34:5 @@ -18,12 +14,7 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(a.is_empty(), "qwqwq"); - | + | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:51:5 @@ -31,12 +22,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!b.is_empty(), "panic1"); - | + | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:54:5 @@ -44,12 +30,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:57:5 @@ -57,12 +38,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:60:5 @@ -70,12 +46,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:63:5 @@ -83,12 +54,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 @@ -96,12 +62,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!a.is_empty(), "with expansion {}", one!()); - | + | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:73:5 From 059e52b96b0127a4b6e7f566a2023482e34c27f6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 20 Oct 2022 17:51:48 +0200 Subject: [PATCH 010/169] rustc_hir_typeck: fix clippy --- clippy_lints/src/escape.rs | 4 ++-- clippy_lints/src/lib.rs | 1 + clippy_lints/src/loops/mut_range_bound.rs | 4 ++-- clippy_lints/src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 4 ++-- clippy_lints/src/operators/assign_op_pattern.rs | 2 +- clippy_lints/src/transmute/utils.rs | 2 +- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/sugg.rs | 7 +++---- clippy_utils/src/usage.rs | 7 +++---- 10 files changed, 17 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index eb0455ae404c1..c9a8307eba4f2 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir; use rustc_hir::intravisit; use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; @@ -178,7 +178,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, ) { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2dcefd78763b7..89ffca8128a9e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -32,6 +32,7 @@ extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_analysis; +extern crate rustc_hir_typeck; extern crate rustc_hir_pretty; extern crate rustc_index; extern crate rustc_infer; diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index db73ab55b37cf..91b321c447479 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -4,7 +4,7 @@ use clippy_utils::{get_enclosing_block, higher, path_to_local}; use if_chain::if_chain; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::{mir::FakeReadCause, ty}; @@ -115,7 +115,7 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, ) { diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 6017941452c06..5cf88bfc8880d 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -8,7 +8,7 @@ use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trai use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; -use rustc_hir_analysis::check::{FnCtxt, Inherited}; +use rustc_hir_typeck::{FnCtxt, Inherited}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 7f881e27dd27c..9c949a28f44d5 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -12,7 +12,7 @@ use rustc_hir::{ BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind, }; use rustc_hir::{HirIdMap, HirIdSet}; -use rustc_hir_analysis::expr_use_visitor as euv; +use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; @@ -342,7 +342,7 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, ) { diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index c7e964cf23e2c..ee9fd94064c08 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -8,7 +8,7 @@ use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::BorrowKind; diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 102f7541c8ce1..70d166c4854c4 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,5 +1,5 @@ use rustc_hir::Expr; -use rustc_hir_analysis::check::{cast, FnCtxt, Inherited}; +use rustc_hir_typeck::{cast, FnCtxt, Inherited}; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e7e3625c078ab..7e42fcc6569b6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -24,7 +24,7 @@ extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; -extern crate rustc_hir_analysis; +extern crate rustc_hir_typeck; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3347342e412b6..5089987ef7200 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -10,7 +10,7 @@ use rustc_ast_pretty::pprust::token_kind_to_string; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::hir::place::ProjectionKind; @@ -1054,11 +1054,10 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, - ) { - } + ) {} } #[cfg(test)] diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index e32bae6ed1fd4..000fb51c01857 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -5,7 +5,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirIdSet; use rustc_hir::{Expr, ExprKind, HirId, Node}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -75,11 +75,10 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, - ) { - } + ) {} } pub struct ParamBindingIdCollector { From 655175494574265257f52f4f7a1e7eabd85cbe77 Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 21 Oct 2022 15:27:25 +0200 Subject: [PATCH 011/169] [`unwrap_used`], [`expect_used`] do not lint in `test` cfg --- clippy_lints/src/methods/expect_used.rs | 4 ++-- clippy_lints/src/methods/unwrap_used.rs | 4 ++-- clippy_lints/src/utils/conf.rs | 4 ++-- tests/ui-toml/expect_used/expect_used.rs | 23 ++++++++++++-------- tests/ui-toml/unwrap_used/unwrap_used.rs | 21 ++++++++++++++---- tests/ui-toml/unwrap_used/unwrap_used.stderr | 6 ++--- 6 files changed, 40 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index d59fefa1ddc0e..0d3c89280465b 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_in_test_function; +use clippy_utils::is_in_cfg_test; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; @@ -27,7 +27,7 @@ pub(super) fn check( let method = if is_err { "expect_err" } else { "expect" }; - if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + if allow_expect_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { return; } diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index ee17f2d7889ee..d11830a24b6c0 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_in_test_function, is_lint_allowed}; +use clippy_utils::{is_in_cfg_test, is_lint_allowed}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -27,7 +27,7 @@ pub(super) fn check( let method_suffix = if is_err { "_err" } else { "" }; - if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + if allow_unwrap_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { return; } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 668123e4d6e39..199dfafebf35a 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -373,11 +373,11 @@ define_Conf! { (max_include_file_size: u64 = 1_000_000), /// Lint: EXPECT_USED. /// - /// Whether `expect` should be allowed in test functions + /// Whether `expect` should be allowed in test cfg (allow_expect_in_tests: bool = false), /// Lint: UNWRAP_USED. /// - /// Whether `unwrap` should be allowed in test functions + /// Whether `unwrap` should be allowed in test cfg (allow_unwrap_in_tests: bool = false), /// Lint: DBG_MACRO. /// diff --git a/tests/ui-toml/expect_used/expect_used.rs b/tests/ui-toml/expect_used/expect_used.rs index 22dcd3ae9d697..bff97d97df77a 100644 --- a/tests/ui-toml/expect_used/expect_used.rs +++ b/tests/ui-toml/expect_used/expect_used.rs @@ -16,14 +16,19 @@ fn main() { expect_result(); } -#[test] -fn test_expect_option() { - let opt = Some(0); - let _ = opt.expect(""); -} +#[cfg(test)] +mod issue9612 { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + let _a: u8 = 2.try_into().unwrap(); + let _a: u8 = 3.try_into().expect(""); -#[test] -fn test_expect_result() { - let res: Result = Ok(0); - let _ = res.expect(""); + util(); + } + + fn util() { + let _a: u8 = 4.try_into().unwrap(); + let _a: u8 = 5.try_into().expect(""); + } } diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs index 0e82fb20e4558..bc8e8c1f0703a 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.rs +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -66,8 +66,21 @@ fn main() { } } -#[test] -fn test() { - let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); - let _ = boxed_slice.get(1).unwrap(); +#[cfg(test)] +mod issue9612 { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + let _a: u8 = 2.try_into().unwrap(); + let _a: u8 = 3.try_into().expect(""); + + util(); + } + + fn util() { + let _a: u8 = 4.try_into().unwrap(); + let _a: u8 = 5.try_into().expect(""); + // should still warn + let _ = Box::new([0]).get(1).unwrap(); + } } diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr index 681b5eaf54dba..2bca88660e1c6 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -188,10 +188,10 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:72:13 + --> $DIR/unwrap_used.rs:84:17 | -LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` +LL | let _ = Box::new([0]).get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&Box::new([0])[1]` error: aborting due to 27 previous errors From 2ed404937f2e9fcd481c717ef4d386d053ea59e3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 16 Oct 2022 18:48:28 +0000 Subject: [PATCH 012/169] Introduce subst_iter and subst_iter_copied on EarlyBinder --- clippy_utils/src/ty.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a15daec7c3ce3..3b5a9ba83568c 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -657,21 +657,18 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O let mut output = None; let lang_items = cx.tcx.lang_items(); - for pred in cx + for (pred, _) in cx .tcx .bound_explicit_item_bounds(ty.item_def_id) - .transpose_iter() - .map(|x| x.map_bound(|(p, _)| p)) + .subst_iter_copied(cx.tcx, ty.substs) { - match pred.0.kind().skip_binder() { + match pred.kind().skip_binder() { PredicateKind::Trait(p) if (lang_items.fn_trait() == Some(p.def_id()) || lang_items.fn_mut_trait() == Some(p.def_id()) || lang_items.fn_once_trait() == Some(p.def_id())) => { - let i = pred - .map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1))) - .subst(cx.tcx, ty.substs); + let i = pred.kind().rebind(p.trait_ref.substs.type_at(1)); if inputs.map_or(false, |inputs| inputs != i) { // Multiple different fn trait impls. Is this even allowed? @@ -684,10 +681,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O // Multiple different fn trait impls. Is this even allowed? return None; } - output = Some( - pred.map_bound(|pred| pred.kind().rebind(p.term.ty().unwrap())) - .subst(cx.tcx, ty.substs), - ); + output = pred.kind().rebind(p.term.ty()).transpose(); }, _ => (), } From a0c82d2bcce1b636c02bb6bf942c05af8dc3ac9a Mon Sep 17 00:00:00 2001 From: kraktus <56031107+kraktus@users.noreply.github.com> Date: Sat, 22 Oct 2022 21:07:05 +0200 Subject: [PATCH 013/169] Explicitly mention [cfg(test)] Co-authored-by: llogiq --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 199dfafebf35a..a10de31556c82 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -373,7 +373,7 @@ define_Conf! { (max_include_file_size: u64 = 1_000_000), /// Lint: EXPECT_USED. /// - /// Whether `expect` should be allowed in test cfg + /// Whether `expect` should be allowed within `#[cfg(test)]` (allow_expect_in_tests: bool = false), /// Lint: UNWRAP_USED. /// From cd0bb7de01d6f516c2e69c03f3bed26aaf167bc8 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 23 Oct 2022 15:18:45 +0200 Subject: [PATCH 014/169] Merge commit '4f142aa1058f14f153f8bfd2d82f04ddb9982388' into clippyup --- .github/workflows/clippy.yml | 1 + .github/workflows/clippy_bors.yml | 1 + .github/workflows/clippy_dev.yml | 2 + CHANGELOG.md | 6 + CONTRIBUTING.md | 2 +- book/src/development/adding_lints.md | 23 +- book/src/development/basics.md | 7 +- clippy_dev/src/serve.rs | 2 +- clippy_dev/src/setup/intellij.rs | 17 +- clippy_dev/src/update_lints.rs | 10 +- clippy_lints/src/booleans.rs | 6 +- clippy_lints/src/box_default.rs | 96 +- clippy_lints/src/casts/as_ptr_cast_mut.rs | 38 + clippy_lints/src/casts/cast_nan_to_int.rs | 28 + clippy_lints/src/casts/mod.rs | 57 +- clippy_lints/src/casts/unnecessary_cast.rs | 3 - clippy_lints/src/comparison_chain.rs | 8 +- clippy_lints/src/default_numeric_fallback.rs | 65 +- clippy_lints/src/dereference.rs | 99 +- clippy_lints/src/derive.rs | 5 +- clippy_lints/src/disallowed_methods.rs | 5 +- clippy_lints/src/entry.rs | 20 +- clippy_lints/src/eta_reduction.rs | 5 +- clippy_lints/src/format_args.rs | 165 +- clippy_lints/src/from_over_into.rs | 176 +- clippy_lints/src/functions/must_use.rs | 10 +- clippy_lints/src/functions/too_many_lines.rs | 5 +- clippy_lints/src/implicit_saturating_sub.rs | 2 +- .../src/invalid_upcast_comparisons.rs | 4 +- clippy_lints/src/large_enum_variant.rs | 5 +- clippy_lints/src/let_underscore.rs | 23 +- clippy_lints/src/lib.register_all.rs | 5 + clippy_lints/src/lib.register_complexity.rs | 2 + clippy_lints/src/lib.register_internal.rs | 32 +- clippy_lints/src/lib.register_lints.rs | 38 +- clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 2 - clippy_lints/src/lib.register_restriction.rs | 2 + clippy_lints/src/lib.register_style.rs | 2 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/lib.rs | 43 +- clippy_lints/src/loops/needless_range_loop.rs | 35 +- .../src/loops/while_let_on_iterator.rs | 5 +- clippy_lints/src/manual_async_fn.rs | 18 +- clippy_lints/src/manual_clamp.rs | 10 +- clippy_lints/src/matches/collapsible_match.rs | 23 +- clippy_lints/src/matches/manual_filter.rs | 153 ++ clippy_lints/src/matches/manual_map.rs | 318 +--- clippy_lints/src/matches/manual_utils.rs | 277 +++ clippy_lints/src/matches/match_same_arms.rs | 6 +- .../src/matches/match_single_binding.rs | 31 +- clippy_lints/src/matches/mod.rs | 40 + clippy_lints/src/matches/single_match.rs | 11 +- clippy_lints/src/methods/into_iter_on_ref.rs | 5 +- .../methods/manual_saturating_arithmetic.rs | 10 +- clippy_lints/src/methods/mod.rs | 25 +- .../src/methods/option_as_ref_deref.rs | 20 +- clippy_lints/src/methods/or_fun_call.rs | 23 +- clippy_lints/src/methods/str_splitn.rs | 4 +- .../src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/misc_early/literal_suffix.rs | 4 +- .../src/misc_early/mixed_case_hex_literals.rs | 4 +- .../src/misc_early/zero_prefixed_literal.rs | 18 +- .../src/mismatching_type_param_order.rs | 5 +- clippy_lints/src/missing_trait_methods.rs | 98 + .../src/mixed_read_write_in_expression.rs | 5 +- clippy_lints/src/needless_for_each.rs | 5 +- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 8 +- clippy_lints/src/non_copy_const.rs | 5 +- .../src/non_octal_unix_permissions.rs | 17 +- clippy_lints/src/nonstandard_macro_braces.rs | 2 +- clippy_lints/src/operators/cmp_owned.rs | 18 +- clippy_lints/src/partial_pub_fields.rs | 81 + clippy_lints/src/ptr.rs | 54 +- clippy_lints/src/ptr_offset_with_cast.rs | 10 +- clippy_lints/src/redundant_clone.rs | 454 +---- clippy_lints/src/ref_option_ref.rs | 3 +- clippy_lints/src/shadow.rs | 5 +- clippy_lints/src/transmute/utils.rs | 24 +- clippy_lints/src/types/rc_buffer.rs | 23 +- .../src/types/redundant_allocation.rs | 8 +- clippy_lints/src/unit_return_expecting_ord.rs | 17 +- clippy_lints/src/unnested_or_patterns.rs | 5 +- clippy_lints/src/unused_io_amount.rs | 5 +- clippy_lints/src/useless_conversion.rs | 5 +- clippy_lints/src/utils/author.rs | 117 +- clippy_lints/src/utils/internal_lints.rs | 1576 +---------------- .../internal_lints/clippy_lints_internal.rs | 49 + .../utils/internal_lints/collapsible_calls.rs | 245 +++ .../internal_lints/compiler_lint_functions.rs | 77 + .../utils/internal_lints/if_chain_style.rs | 164 ++ .../interning_defined_symbol.rs | 239 +++ .../src/utils/internal_lints/invalid_paths.rs | 108 ++ .../internal_lints/lint_without_lint_pass.rs | 342 ++++ .../internal_lints/metadata_collector.rs | 8 +- .../utils/internal_lints/msrv_attr_impl.rs | 63 + .../internal_lints/outer_expn_data_pass.rs | 62 + .../src/utils/internal_lints/produce_ice.rs | 37 + .../internal_lints/unnecessary_def_path.rs | 343 ++++ clippy_utils/src/attrs.rs | 2 +- clippy_utils/src/consts.rs | 44 +- clippy_utils/src/eager_or_lazy.rs | 2 +- clippy_utils/src/lib.rs | 48 +- clippy_utils/src/macros.rs | 43 +- clippy_utils/src/mir/maybe_storage_live.rs | 52 + clippy_utils/src/mir/mod.rs | 164 ++ clippy_utils/src/mir/possible_borrower.rs | 241 +++ clippy_utils/src/mir/possible_origin.rs | 59 + clippy_utils/src/mir/transitive_relation.rs | 29 + clippy_utils/src/numeric_literal.rs | 7 +- clippy_utils/src/paths.rs | 36 - clippy_utils/src/sugg.rs | 13 +- lintcheck/src/config.rs | 5 +- lintcheck/src/driver.rs | 2 +- lintcheck/src/main.rs | 162 +- lintcheck/src/recursive.rs | 8 +- rust-toolchain | 2 +- src/docs.rs | 6 + src/docs/as_ptr_cast_mut.txt | 19 + src/docs/box_default.txt | 6 - src/docs/cast_nan_to_int.txt | 15 + src/docs/manual_filter.txt | 21 + src/docs/missing_trait_methods.txt | 40 + src/docs/partial_pub_fields.txt | 27 + src/docs/unused_format_specs.txt | 24 + tests/compile-test.rs | 2 +- tests/dogfood.rs | 9 +- tests/ui-internal/custom_ice_message.stderr | 2 +- tests/ui-internal/invalid_paths.rs | 2 +- tests/ui-internal/unnecessary_def_path.fixed | 6 +- tests/ui-internal/unnecessary_def_path.rs | 6 +- .../unnecessary_def_path_hardcoded_path.rs | 16 + ...unnecessary_def_path_hardcoded_path.stderr | 27 + tests/ui/as_ptr_cast_mut.rs | 37 + tests/ui/as_ptr_cast_mut.stderr | 16 + tests/ui/author.stdout | 24 +- tests/ui/author/blocks.stdout | 116 +- tests/ui/author/call.stdout | 28 +- tests/ui/author/if.stdout | 92 +- tests/ui/author/issue_3849.stdout | 24 +- tests/ui/author/loop.stdout | 202 +-- tests/ui/author/matches.stdout | 72 +- tests/ui/author/repeat.stdout | 20 +- tests/ui/author/struct.stdout | 112 +- tests/ui/box_default.fixed | 57 + tests/ui/box_default.rs | 28 +- tests/ui/box_default.stderr | 83 +- tests/ui/box_default_no_std.rs | 33 + tests/ui/cast_abs_to_unsigned.fixed | 18 +- tests/ui/cast_abs_to_unsigned.rs | 18 +- tests/ui/cast_abs_to_unsigned.stderr | 42 +- tests/ui/cast_lossless_bool.fixed | 13 + tests/ui/cast_lossless_bool.rs | 13 + tests/ui/cast_lossless_bool.stderr | 34 +- tests/ui/cast_nan_to_int.rs | 18 + tests/ui/cast_nan_to_int.stderr | 51 + tests/ui/cfg_attr_rustfmt.fixed | 16 +- tests/ui/cfg_attr_rustfmt.rs | 16 +- tests/ui/cfg_attr_rustfmt.stderr | 8 +- tests/ui/checked_conversions.fixed | 16 + tests/ui/checked_conversions.rs | 16 + tests/ui/checked_conversions.stderr | 40 +- tests/ui/cloned_instead_of_copied.fixed | 24 + tests/ui/cloned_instead_of_copied.rs | 24 + tests/ui/cloned_instead_of_copied.stderr | 30 +- tests/ui/collapsible_match.rs | 21 + tests/ui/collapsible_match.stderr | 34 +- tests/ui/crashes/ice-9625.rs | 4 + tests/ui/default_numeric_fallback_f64.fixed | 9 + tests/ui/default_numeric_fallback_f64.rs | 9 + tests/ui/default_numeric_fallback_f64.stderr | 34 +- tests/ui/default_numeric_fallback_i32.fixed | 10 + tests/ui/default_numeric_fallback_i32.rs | 10 + tests/ui/default_numeric_fallback_i32.stderr | 34 +- tests/ui/err_expect.fixed | 17 + tests/ui/err_expect.rs | 17 + tests/ui/err_expect.stderr | 10 +- tests/ui/filter_map_next_fixable.fixed | 16 + tests/ui/filter_map_next_fixable.rs | 16 + tests/ui/filter_map_next_fixable.stderr | 10 +- tests/ui/format_args.fixed | 3 + tests/ui/format_args.rs | 3 + tests/ui/format_args.stderr | 60 +- tests/ui/from_over_into.fixed | 87 + tests/ui/from_over_into.rs | 66 + tests/ui/from_over_into.stderr | 70 +- tests/ui/from_over_into_unfixable.rs | 35 + tests/ui/from_over_into_unfixable.stderr | 29 + tests/ui/implicit_saturating_sub.fixed | 50 + tests/ui/implicit_saturating_sub.rs | 50 + tests/ui/implicit_saturating_sub.stderr | 46 +- tests/ui/literals.rs | 7 + tests/ui/literals.stderr | 35 +- tests/ui/manual_assert.edition2018.fixed | 39 +- tests/ui/manual_assert.edition2018.stderr | 67 +- tests/ui/manual_assert.edition2021.fixed | 4 +- tests/ui/manual_assert.rs | 4 +- tests/ui/manual_clamp.rs | 27 + tests/ui/manual_clamp.stderr | 85 +- tests/ui/manual_filter.fixed | 119 ++ tests/ui/manual_filter.rs | 243 +++ tests/ui/manual_filter.stderr | 191 ++ tests/ui/manual_rem_euclid.fixed | 30 + tests/ui/manual_rem_euclid.rs | 30 + tests/ui/manual_rem_euclid.stderr | 30 +- tests/ui/manual_strip.rs | 19 + tests/ui/manual_strip.stderr | 47 +- tests/ui/map_unwrap_or.rs | 20 +- tests/ui/map_unwrap_or.stderr | 30 +- tests/ui/match_expr_like_matches_macro.fixed | 16 + tests/ui/match_expr_like_matches_macro.rs | 19 + tests/ui/match_expr_like_matches_macro.stderr | 38 +- tests/ui/match_overlapping_arm.rs | 1 - tests/ui/match_overlapping_arm.stderr | 32 +- tests/ui/match_single_binding.fixed | 9 + tests/ui/match_single_binding.rs | 8 + tests/ui/match_single_binding.stderr | 19 +- .../ui/match_wild_err_arm.edition2021.stderr | 35 - tests/ui/match_wild_err_arm.rs | 3 - ...n2018.stderr => match_wild_err_arm.stderr} | 8 +- tests/ui/mem_replace.fixed | 18 +- tests/ui/mem_replace.rs | 18 +- tests/ui/mem_replace.stderr | 46 +- tests/ui/min_rust_version_attr.rs | 239 +-- tests/ui/min_rust_version_attr.stderr | 46 +- tests/ui/min_rust_version_invalid_attr.rs | 14 + tests/ui/min_rust_version_invalid_attr.stderr | 44 +- .../min_rust_version_multiple_inner_attr.rs | 11 - ...in_rust_version_multiple_inner_attr.stderr | 38 - 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 - .../ui/missing_const_for_fn/could_be_const.rs | 12 + .../could_be_const.stderr | 12 +- tests/ui/missing_trait_methods.rs | 50 + tests/ui/missing_trait_methods.stderr | 27 + tests/ui/needless_borrow.fixed | 62 +- tests/ui/needless_borrow.rs | 60 +- tests/ui/needless_borrow.stderr | 96 +- tests/ui/option_as_ref_deref.fixed | 17 +- tests/ui/option_as_ref_deref.rs | 17 +- tests/ui/option_as_ref_deref.stderr | 42 +- tests/ui/or_fun_call.fixed | 11 + tests/ui/or_fun_call.rs | 11 + tests/ui/partial_pub_fields.rs | 40 + tests/ui/partial_pub_fields.stderr | 35 + tests/ui/ptr_arg.rs | 30 +- tests/ui/ptr_arg.stderr | 20 +- tests/ui/range_contains.fixed | 26 +- tests/ui/range_contains.rs | 26 +- tests/ui/range_contains.stderr | 48 +- tests/ui/redundant_field_names.fixed | 16 + tests/ui/redundant_field_names.rs | 16 + tests/ui/redundant_field_names.stderr | 22 +- tests/ui/redundant_static_lifetimes.fixed | 13 + tests/ui/redundant_static_lifetimes.rs | 13 + tests/ui/redundant_static_lifetimes.stderr | 40 +- tests/ui/ref_option_ref.rs | 5 + tests/ui/uninlined_format_args.fixed | 13 + tests/ui/uninlined_format_args.rs | 13 + tests/ui/uninlined_format_args.stderr | 40 +- ...nlined_format_args_panic.edition2018.fixed | 29 + ...lined_format_args_panic.edition2018.stderr | 15 + ...nlined_format_args_panic.edition2021.fixed | 29 + ...lined_format_args_panic.edition2021.stderr | 51 + tests/ui/uninlined_format_args_panic.rs | 29 + tests/ui/unnecessary_cast.fixed | 4 + tests/ui/unnecessary_cast.rs | 4 + tests/ui/unnecessary_to_owned.fixed | 2 +- tests/ui/unnecessary_to_owned.rs | 2 +- tests/ui/unnested_or_patterns.fixed | 16 +- tests/ui/unnested_or_patterns.rs | 16 +- tests/ui/unnested_or_patterns.stderr | 13 +- tests/ui/unused_format_specs.fixed | 18 + tests/ui/unused_format_specs.rs | 18 + tests/ui/unused_format_specs.stderr | 54 + tests/ui/unused_format_specs_unfixable.rs | 30 + tests/ui/unused_format_specs_unfixable.stderr | 69 + tests/ui/use_self.fixed | 33 + tests/ui/use_self.rs | 33 + tests/ui/use_self.stderr | 90 +- tests/versioncheck.rs | 2 +- util/gh-pages/index.html | 6 + util/gh-pages/script.js | 15 +- 284 files changed, 8511 insertions(+), 4206 deletions(-) create mode 100644 clippy_lints/src/casts/as_ptr_cast_mut.rs create mode 100644 clippy_lints/src/casts/cast_nan_to_int.rs create mode 100644 clippy_lints/src/matches/manual_filter.rs create mode 100644 clippy_lints/src/matches/manual_utils.rs create mode 100644 clippy_lints/src/missing_trait_methods.rs create mode 100644 clippy_lints/src/partial_pub_fields.rs create mode 100644 clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs create mode 100644 clippy_lints/src/utils/internal_lints/collapsible_calls.rs create mode 100644 clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs create mode 100644 clippy_lints/src/utils/internal_lints/if_chain_style.rs create mode 100644 clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs create mode 100644 clippy_lints/src/utils/internal_lints/invalid_paths.rs create mode 100644 clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs create mode 100644 clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs create mode 100644 clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs create mode 100644 clippy_lints/src/utils/internal_lints/produce_ice.rs create mode 100644 clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs create mode 100644 clippy_utils/src/mir/maybe_storage_live.rs create mode 100644 clippy_utils/src/mir/mod.rs create mode 100644 clippy_utils/src/mir/possible_borrower.rs create mode 100644 clippy_utils/src/mir/possible_origin.rs create mode 100644 clippy_utils/src/mir/transitive_relation.rs create mode 100644 src/docs/as_ptr_cast_mut.txt create mode 100644 src/docs/cast_nan_to_int.txt create mode 100644 src/docs/manual_filter.txt create mode 100644 src/docs/missing_trait_methods.txt create mode 100644 src/docs/partial_pub_fields.txt create mode 100644 src/docs/unused_format_specs.txt create mode 100644 tests/ui-internal/unnecessary_def_path_hardcoded_path.rs create mode 100644 tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr create mode 100644 tests/ui/as_ptr_cast_mut.rs create mode 100644 tests/ui/as_ptr_cast_mut.stderr create mode 100644 tests/ui/box_default.fixed create mode 100644 tests/ui/box_default_no_std.rs create mode 100644 tests/ui/cast_nan_to_int.rs create mode 100644 tests/ui/cast_nan_to_int.stderr create mode 100644 tests/ui/crashes/ice-9625.rs create mode 100644 tests/ui/from_over_into.fixed create mode 100644 tests/ui/from_over_into_unfixable.rs create mode 100644 tests/ui/from_over_into_unfixable.stderr create mode 100644 tests/ui/manual_filter.fixed create mode 100644 tests/ui/manual_filter.rs create mode 100644 tests/ui/manual_filter.stderr delete mode 100644 tests/ui/match_wild_err_arm.edition2021.stderr rename tests/ui/{match_wild_err_arm.edition2018.stderr => match_wild_err_arm.stderr} (86%) delete mode 100644 tests/ui/min_rust_version_multiple_inner_attr.rs delete mode 100644 tests/ui/min_rust_version_multiple_inner_attr.stderr delete mode 100644 tests/ui/min_rust_version_no_patch.rs delete mode 100644 tests/ui/min_rust_version_outer_attr.rs delete mode 100644 tests/ui/min_rust_version_outer_attr.stderr create mode 100644 tests/ui/missing_trait_methods.rs create mode 100644 tests/ui/missing_trait_methods.stderr create mode 100644 tests/ui/partial_pub_fields.rs create mode 100644 tests/ui/partial_pub_fields.stderr create mode 100644 tests/ui/uninlined_format_args_panic.edition2018.fixed create mode 100644 tests/ui/uninlined_format_args_panic.edition2018.stderr create mode 100644 tests/ui/uninlined_format_args_panic.edition2021.fixed create mode 100644 tests/ui/uninlined_format_args_panic.edition2021.stderr create mode 100644 tests/ui/uninlined_format_args_panic.rs create mode 100644 tests/ui/unused_format_specs.fixed create mode 100644 tests/ui/unused_format_specs.rs create mode 100644 tests/ui/unused_format_specs.stderr create mode 100644 tests/ui/unused_format_specs_unfixable.rs create mode 100644 tests/ui/unused_format_specs_unfixable.stderr diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index fac2c99714d9b..b992130119713 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -25,6 +25,7 @@ env: CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 + CARGO_UNSTABLE_SPARSE_REGISTRY: true jobs: base: diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 30607af490124..6448b2d4068de 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -11,6 +11,7 @@ env: CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 + CARGO_UNSTABLE_SPARSE_REGISTRY: true defaults: run: diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 22051093c9cf9..14f20212adda5 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -15,6 +15,8 @@ on: env: RUST_BACKTRACE: 1 + CARGO_INCREMENTAL: 0 + CARGO_UNSTABLE_SPARSE_REGISTRY: true jobs: clippy_dev: diff --git a/CHANGELOG.md b/CHANGELOG.md index 42615179f7053..2d7bda27e4fcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3735,6 +3735,7 @@ Released 2018-09-13 [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions +[`as_ptr_cast_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_ptr_cast_mut [`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states @@ -3772,6 +3773,7 @@ Released 2018-09-13 [`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor [`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless +[`cast_nan_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_nan_to_int [`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 [`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss @@ -3988,6 +3990,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp +[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map @@ -4046,6 +4049,7 @@ Released 2018-09-13 [`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 [`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop +[`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods [`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 [`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression @@ -4131,6 +4135,7 @@ Released 2018-09-13 [`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 +[`partial_pub_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#partial_pub_fields [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite @@ -4312,6 +4317,7 @@ Released 2018-09-13 [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice [`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect +[`unused_format_specs`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_format_specs [`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_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c977b2cacab5..85f94a74ad91d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,7 +29,7 @@ All contributors are expected to follow the [Rust Code of Conduct]. ## The Clippy book -If you're new to Clippy and don't know where to start the [Clippy book] includes +If you're new to Clippy and don't know where to start, the [Clippy book] includes a [developer guide] and is a good place to start your journey. [Clippy book]: https://doc.rust-lang.org/nightly/clippy/index.html diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index b1e843bc7f4c8..3c3f368a529b1 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -478,8 +478,27 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { ``` Once the `msrv` is added to the lint, a relevant test case should be added to -`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted -if the project's MSRV is lower. +the lint's test file, `tests/ui/manual_strip.rs` in this example. It should +have a case for the version below the MSRV and one with the same contents but +for the MSRV version itself. + +```rust +#![feature(custom_inner_attributes)] + +... + +fn msrv_1_44() { + #![clippy::msrv = "1.44"] + + /* something that would trigger the lint */ +} + +fn msrv_1_45() { + #![clippy::msrv = "1.45"] + + /* something that would trigger the lint */ +} +``` As a last step, the lint should be added to the lint documentation. This is done in `clippy_lints/src/utils/conf.rs`: diff --git a/book/src/development/basics.md b/book/src/development/basics.md index 44ba6e32755e3..6fb53236e6f1a 100644 --- a/book/src/development/basics.md +++ b/book/src/development/basics.md @@ -69,7 +69,7 @@ the reference file with: cargo dev bless ``` -For example, this is necessary, if you fix a typo in an error message of a lint +For example, this is necessary if you fix a typo in an error message of a lint, or if you modify a test file to add a test case. > _Note:_ This command may update more files than you intended. In that case @@ -101,8 +101,9 @@ cargo dev setup intellij cargo dev dogfood ``` -More about intellij command usage and reasons -[here](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust) +More about [intellij] command usage and reasons. + +[intellij]: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust ## lintcheck diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index 2e0794f12fa19..535c25e69f1bb 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -49,7 +49,7 @@ fn mtime(path: impl AsRef) -> SystemTime { .into_iter() .flatten() .flatten() - .map(|entry| mtime(&entry.path())) + .map(|entry| mtime(entry.path())) .max() .unwrap_or(SystemTime::UNIX_EPOCH) } else { diff --git a/clippy_dev/src/setup/intellij.rs b/clippy_dev/src/setup/intellij.rs index b64e79733eb24..efdb158c21e9b 100644 --- a/clippy_dev/src/setup/intellij.rs +++ b/clippy_dev/src/setup/intellij.rs @@ -36,9 +36,8 @@ impl ClippyProjectInfo { } pub fn setup_rustc_src(rustc_path: &str) { - let rustc_source_dir = match check_and_get_rustc_dir(rustc_path) { - Ok(path) => path, - Err(_) => return, + let Ok(rustc_source_dir) = check_and_get_rustc_dir(rustc_path) else { + return }; for project in CLIPPY_PROJECTS { @@ -172,14 +171,10 @@ pub fn remove_rustc_src() { } fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool { - let mut cargo_content = if let Ok(content) = read_project_file(project.cargo_file) { - content - } else { + let Ok(mut cargo_content) = read_project_file(project.cargo_file) else { return false; }; - let section_start = if let Some(section_start) = cargo_content.find(RUSTC_PATH_SECTION) { - section_start - } else { + let Some(section_start) = cargo_content.find(RUSTC_PATH_SECTION) else { println!( "info: dependencies could not be found in `{}` for {}, skipping file", project.cargo_file, project.name @@ -187,9 +182,7 @@ fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool { return true; }; - let end_point = if let Some(end_point) = cargo_content.find(DEPENDENCIES_SECTION) { - end_point - } else { + let Some(end_point) = cargo_content.find(DEPENDENCIES_SECTION) else { eprintln!( "error: the end of the rustc dependencies section could not be found in `{}`", project.cargo_file diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 0eb443167ecf3..e690bc369cd43 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -128,7 +128,7 @@ fn generate_lint_files( for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { let content = gen_lint_group_list(&lint_group, lints.iter()); process_file( - &format!("clippy_lints/src/lib.register_{lint_group}.rs"), + format!("clippy_lints/src/lib.register_{lint_group}.rs"), update_mode, &content, ); @@ -869,13 +869,11 @@ fn clippy_lints_src_files() -> impl Iterator { macro_rules! match_tokens { ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => { { - $($(let $capture =)? if let Some(LintDeclSearchResult { + $(#[allow(clippy::redundant_pattern)] let Some(LintDeclSearchResult { token_kind: TokenKind::$token $({$($fields)*})?, - content: _x, + content: $($capture @)? _, .. - }) = $iter.next() { - _x - } else { + }) = $iter.next() else { continue; };)* #[allow(clippy::unused_unit)] diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 2a15cbc7a3c3b..08164c0b654e2 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::eq_expr_value; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{eq_expr_value, get_trait_def_id, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -483,7 +483,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { fn implements_ord<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) + cx.tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])) } struct NotSimplificationVisitor<'a, 'tcx> { diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 792183ac40814..36daceabe0bea 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -1,5 +1,12 @@ -use clippy_utils::{diagnostics::span_lint_and_help, is_default_equivalent, path_def_id}; -use rustc_hir::{Expr, ExprKind, QPath}; +use clippy_utils::{ + diagnostics::span_lint_and_sugg, get_parent_node, is_default_equivalent, macros::macro_backtrace, match_path, + path_def_id, paths, ty::expr_sig, +}; +use rustc_errors::Applicability; +use rustc_hir::{ + intravisit::{walk_ty, Visitor}, + Block, Expr, ExprKind, Local, Node, QPath, TyKind, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -15,12 +22,6 @@ declare_clippy_lint! { /// Second, `Box::default()` can be faster /// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). /// - /// ### Known problems - /// The lint may miss some cases (e.g. Box::new(String::from(""))). - /// On the other hand, it will trigger on cases where the `default` - /// code comes from a macro that does something different based on - /// e.g. target operating system. - /// /// ### Example /// ```rust /// let x: Box = Box::new(Default::default()); @@ -41,21 +42,88 @@ impl LateLintPass<'_> for BoxDefault { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(box_new, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind - && let ExprKind::Call(..) = arg.kind + && let ExprKind::Call(arg_path, ..) = arg.kind && !in_external_macro(cx.sess(), expr.span) - && expr.span.eq_ctxt(arg.span) + && (expr.span.eq_ctxt(arg.span) || is_vec_expn(cx, arg)) && seg.ident.name == sym::new - && path_def_id(cx, ty) == cx.tcx.lang_items().owned_box() + && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box()) && is_default_equivalent(cx, arg) { - span_lint_and_help( + let arg_ty = cx.typeck_results().expr_ty(arg); + span_lint_and_sugg( cx, BOX_DEFAULT, expr.span, "`Box::new(_)` of default value", - None, - "use `Box::default()` instead", + "try", + if is_plain_default(arg_path) || given_type(cx, expr) { + "Box::default()".into() + } else { + format!("Box::<{arg_ty}>::default()") + }, + Applicability::MachineApplicable ); } } } + +fn is_plain_default(arg_path: &Expr<'_>) -> bool { + // we need to match the actual path so we don't match e.g. "u8::default" + if let ExprKind::Path(QPath::Resolved(None, path)) = &arg_path.kind { + // avoid generic parameters + match_path(path, &paths::DEFAULT_TRAIT_METHOD) && path.segments.iter().all(|seg| seg.args.is_none()) + } else { + false + } +} + +fn is_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + macro_backtrace(expr.span) + .next() + .map_or(false, |call| cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id)) +} + +#[derive(Default)] +struct InferVisitor(bool); + +impl<'tcx> Visitor<'tcx> for InferVisitor { + fn visit_ty(&mut self, t: &rustc_hir::Ty<'_>) { + self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..)); + if !self.0 { + walk_ty(self, t); + } + } +} + +fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Local(Local { ty: Some(ty), .. })) => { + let mut v = InferVisitor::default(); + v.visit_ty(ty); + !v.0 + }, + Some( + Node::Expr(Expr { + kind: ExprKind::Call(path, args), + .. + }) | Node::Block(Block { + expr: + Some(Expr { + kind: ExprKind::Call(path, args), + .. + }), + .. + }), + ) => { + if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) && + let Some(sig) = expr_sig(cx, path) && + let Some(input) = sig.input(index) + { + input.no_bound_vars().is_some() + } else { + false + } + }, + _ => false, + } +} diff --git a/clippy_lints/src/casts/as_ptr_cast_mut.rs b/clippy_lints/src/casts/as_ptr_cast_mut.rs new file mode 100644 index 0000000000000..9409f4844f54b --- /dev/null +++ b/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::{ + mir::Mutability, + ty::{self, Ty, TypeAndMut}, +}; + +use super::AS_PTR_CAST_MUT; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) { + if let ty::RawPtr(ptrty @ TypeAndMut { mutbl: Mutability::Mut, .. }) = cast_to.kind() + && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) = + cx.typeck_results().node_type(cast_expr.hir_id).kind() + && let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind + && method_name.ident.name == rustc_span::sym::as_ptr + && let Some(as_ptr_did) = cx.typeck_results().type_dependent_def_id(cast_expr.peel_blocks().hir_id) + && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did) + && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next() + && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind() + && let Some(recv) = snippet_opt(cx, receiver.span) + { + // `as_mut_ptr` might not exist + let applicability = Applicability::MaybeIncorrect; + + span_lint_and_sugg( + cx, + AS_PTR_CAST_MUT, + expr.span, + &format!("casting the result of `as_ptr` to *{ptrty}"), + "replace with", + format!("{recv}.as_mut_ptr()"), + applicability + ); + } +} diff --git a/clippy_lints/src/casts/cast_nan_to_int.rs b/clippy_lints/src/casts/cast_nan_to_int.rs new file mode 100644 index 0000000000000..322dc41b3a197 --- /dev/null +++ b/clippy_lints/src/casts/cast_nan_to_int.rs @@ -0,0 +1,28 @@ +use super::CAST_NAN_TO_INT; + +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_note; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, from_ty: Ty<'_>, to_ty: Ty<'_>) { + if from_ty.is_floating_point() && to_ty.is_integral() && is_known_nan(cx, cast_expr) { + span_lint_and_note( + cx, + CAST_NAN_TO_INT, + expr.span, + &format!("casting a known NaN to {to_ty}"), + None, + "this always evaluates to 0", + ); + } +} + +fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + match constant(cx, cx.typeck_results(), e) { + Some((Constant::F64(n), _)) => n.is_nan(), + Some((Constant::F32(n), _)) => n.is_nan(), + _ => false, + } +} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index cc5d346b954e3..b72c4c772f1ce 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -1,8 +1,10 @@ +mod as_ptr_cast_mut; mod as_underscore; mod borrow_as_ptr; mod cast_abs_to_unsigned; mod cast_enum_constructor; mod cast_lossless; +mod cast_nan_to_int; mod cast_possible_truncation; mod cast_possible_wrap; mod cast_precision_loss; @@ -569,6 +571,7 @@ declare_clippy_lint! { pedantic, "borrowing just to cast to a raw pointer" } + declare_clippy_lint! { /// ### What it does /// Checks for a raw slice being cast to a slice pointer @@ -596,6 +599,54 @@ declare_clippy_lint! { "casting a slice created from a pointer and length to a slice pointer" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer + /// + /// ### Why is this bad? + /// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior + /// mutability is used, making it unlikely that having it as a mutable pointer is correct. + /// + /// ### Example + /// ```rust + /// let string = String::with_capacity(1); + /// let ptr = string.as_ptr() as *mut u8; + /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR + /// ``` + /// Use instead: + /// ```rust + /// let mut string = String::with_capacity(1); + /// let ptr = string.as_mut_ptr(); + /// unsafe { ptr.write(4) }; + /// ``` + #[clippy::version = "1.66.0"] + pub AS_PTR_CAST_MUT, + nursery, + "casting the result of the `&self`-taking `as_ptr` to a mutabe pointer" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for a known NaN float being cast to an integer + /// + /// ### Why is this bad? + /// NaNs are cast into zero, so one could simply use this and make the + /// code more readable. The lint could also hint at a programmer error. + /// + /// ### Example + /// ```rust,ignore + /// let _: (0.0_f32 / 0.0) as u64; + /// ``` + /// Use instead: + /// ```rust,ignore + /// let _: = 0_u64; + /// ``` + #[clippy::version = "1.64.0"] + pub CAST_NAN_TO_INT, + suspicious, + "casting a known floating-point NaN into an integer" +} + pub struct Casts { msrv: Option, } @@ -627,7 +678,9 @@ impl_lint_pass!(Casts => [ CAST_ABS_TO_UNSIGNED, AS_UNDERSCORE, BORROW_AS_PTR, - CAST_SLICE_FROM_RAW_PARTS + CAST_SLICE_FROM_RAW_PARTS, + AS_PTR_CAST_MUT, + CAST_NAN_TO_INT, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -653,6 +706,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv); + as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); @@ -664,6 +718,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); + cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to); } cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 21ed7f4844cc5..c8596987e4d71 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -59,9 +59,6 @@ pub(super) fn check<'tcx>( lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); return false; }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => { - return false; - }, LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index a05b41eb3ab52..0fe973b49a355 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq}; +use clippy_utils::{if_sequence, in_constant, is_else_clause, SpanlessEq}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -106,7 +107,10 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { // Check that the type being compared implements `core::cmp::Ord` let ty = cx.typeck_results().expr_ty(lhs1); - let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])); + let is_ord = cx + .tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])); if !is_ord { return; diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 3ed9cd36a2292..03460689e19ad 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; +use clippy_utils::{get_parent_node, numeric_literal}; use if_chain::if_chain; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::{ intravisit::{walk_expr, walk_stmt, Visitor}, - Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind, + Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::{ @@ -55,22 +55,31 @@ declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { - let mut visitor = NumericFallbackVisitor::new(cx); + let is_parent_const = if let Some(Node::Item(item)) = get_parent_node(cx.tcx, body.id().hir_id) { + matches!(item.kind, ItemKind::Const(..)) + } else { + false + }; + let mut visitor = NumericFallbackVisitor::new(cx, is_parent_const); visitor.visit_body(body); } } struct NumericFallbackVisitor<'a, 'tcx> { /// Stack manages type bound of exprs. The top element holds current expr type. - ty_bounds: Vec>, + ty_bounds: Vec, cx: &'a LateContext<'tcx>, } impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { + fn new(cx: &'a LateContext<'tcx>, is_parent_const: bool) -> Self { Self { - ty_bounds: vec![TyBound::Nothing], + ty_bounds: vec![if is_parent_const { + ExplicitTyBound(true) + } else { + ExplicitTyBound(false) + }], cx, } } @@ -79,10 +88,9 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { if_chain! { if !in_external_macro(self.cx.sess(), lit.span); - if let Some(ty_bound) = self.ty_bounds.last(); + if matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false))); if matches!(lit.node, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); - if !ty_bound.is_numeric(); then { let (suffix, is_float) = match lit_ty.kind() { ty::Int(IntTy::I32) => ("i32", false), @@ -123,7 +131,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) { for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) { // Push found arg type, then visit arg. - self.ty_bounds.push(TyBound::Ty(*bound)); + self.ty_bounds.push((*bound).into()); self.visit_expr(expr); self.ty_bounds.pop(); } @@ -135,7 +143,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { 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 iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) { - self.ty_bounds.push(TyBound::Ty(*bound)); + self.ty_bounds.push((*bound).into()); self.visit_expr(expr); self.ty_bounds.pop(); } @@ -169,7 +177,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { // Visit base with no bound. if let Some(base) = base { - self.ty_bounds.push(TyBound::Nothing); + self.ty_bounds.push(ExplicitTyBound(false)); self.visit_expr(base); self.ty_bounds.pop(); } @@ -192,15 +200,10 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { 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); - } - }, + // we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric` + StmtKind::Local(local) => self.ty_bounds.push(ExplicitTyBound(local.ty.is_some())), - _ => self.ty_bounds.push(TyBound::Nothing), + _ => self.ty_bounds.push(ExplicitTyBound(false)), } walk_stmt(self, stmt); @@ -218,28 +221,18 @@ fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option { - Any, - Ty(Ty<'tcx>), - Nothing, -} +struct ExplicitTyBound(pub bool); -impl<'tcx> TyBound<'tcx> { - fn is_numeric(self) -> bool { - match self { - TyBound::Any => true, - TyBound::Ty(t) => t.is_numeric(), - TyBound::Nothing => false, - } +impl<'tcx> From> for ExplicitTyBound { + fn from(v: Ty<'tcx>) -> Self { + Self(v.is_numeric()) } } -impl<'tcx> From>> for TyBound<'tcx> { +impl<'tcx> From>> for ExplicitTyBound { fn from(v: Option>) -> Self { - match v { - Some(t) => TyBound::Ty(t), - None => TyBound::Nothing, - } + Self(v.map_or(false, Ty::is_numeric)) } } diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 02a16f765b732..a95d9f5390de3 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res}; @@ -11,13 +12,16 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, - GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, - Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp, + self as hir, + def_id::{DefId, LocalDefId}, + BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, ImplItem, + ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem, + TraitItemKind, TyKind, UnOp, }; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ self, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, @@ -141,7 +145,7 @@ declare_clippy_lint! { "dereferencing when the compiler would automatically dereference" } -impl_lint_pass!(Dereferencing => [ +impl_lint_pass!(Dereferencing<'_> => [ EXPLICIT_DEREF_METHODS, NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE, @@ -149,7 +153,7 @@ impl_lint_pass!(Dereferencing => [ ]); #[derive(Default)] -pub struct Dereferencing { +pub struct Dereferencing<'tcx> { state: Option<(State, StateData)>, // While parsing a `deref` method call in ufcs form, the path to the function is itself an @@ -170,11 +174,16 @@ pub struct Dereferencing { /// e.g. `m!(x) | Foo::Bar(ref x)` ref_locals: FxIndexMap>, + /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by + /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead + /// be moved. + possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + // `IntoIterator` for arrays requires Rust 1.53. msrv: Option, } -impl Dereferencing { +impl<'tcx> Dereferencing<'tcx> { #[must_use] pub fn new(msrv: Option) -> Self { Self { @@ -244,7 +253,7 @@ struct RefPat { hir_id: HirId, } -impl<'tcx> LateLintPass<'tcx> for Dereferencing { +impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Skip path expressions from deref calls. e.g. `Deref::deref(e)` @@ -278,7 +287,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); - let (position, adjustments) = walk_parents(cx, expr, self.msrv); + let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv); match kind { RefOp::Deref => { if let Position::FieldAccess { @@ -550,6 +559,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { } fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| { + local_def_id == cx.tcx.hir().body_owner_def_id(body.id()) + }) { + self.possible_borrowers.pop(); + } + if Some(body.id()) == self.current_body { for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) { let replacements = pat.replacements; @@ -682,6 +697,7 @@ impl Position { #[expect(clippy::too_many_lines)] fn walk_parents<'tcx>( cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, e: &'tcx Expr<'_>, msrv: Option, ) -> (Position, &'tcx [Adjustment<'tcx>]) { @@ -796,7 +812,16 @@ fn walk_parents<'tcx>( Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()), None => { if let ty::Param(param_ty) = ty.skip_binder().kind() { - needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv) + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i, + *param_ty, + e, + precedence, + msrv, + ) } else { ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) .position_for_arg() @@ -843,7 +868,16 @@ fn walk_parents<'tcx>( args.iter().position(|arg| arg.hir_id == child_id).map(|i| { let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1]; if let ty::Param(param_ty) = ty.kind() { - needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv) + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i + 1, + *param_ty, + e, + precedence, + msrv, + ) } else { ty_auto_deref_stability( cx, @@ -1017,8 +1051,10 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { // If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`. // The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to // be moved, but it cannot be. +#[expect(clippy::too_many_arguments)] fn needless_borrow_impl_arg_position<'tcx>( cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, parent: &Expr<'tcx>, arg_index: usize, param_ty: ParamTy, @@ -1081,10 +1117,13 @@ fn needless_borrow_impl_arg_position<'tcx>( // elements are modified each time `check_referent` is called. let mut substs_with_referent_ty = substs_with_expr_ty.to_vec(); - let mut check_referent = |referent| { + let mut check_reference_and_referent = |reference, referent| { let referent_ty = cx.typeck_results().expr_ty(referent); - if !is_copy(cx, referent_ty) { + if !is_copy(cx, referent_ty) + && (referent_ty.has_significant_drop(cx.tcx, cx.param_env) + || !referent_used_exactly_once(cx, possible_borrowers, reference)) + { return false; } @@ -1125,7 +1164,7 @@ fn needless_borrow_impl_arg_position<'tcx>( let mut needless_borrow = false; while let ExprKind::AddrOf(_, _, referent) = expr.kind { - if !check_referent(referent) { + if !check_reference_and_referent(expr, referent) { break; } expr = referent; @@ -1153,6 +1192,36 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { }) } +fn referent_used_exactly_once<'tcx>( + cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + reference: &Expr<'tcx>, +) -> bool { + let mir = enclosing_mir(cx.tcx, reference.hir_id); + if let Some(local) = expr_local(cx.tcx, reference) + && let [location] = *local_assignments(mir, local).as_slice() + && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) + && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind + && !place.has_deref() + { + let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); + if possible_borrowers + .last() + .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) + { + possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); + } + let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; + // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is + // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of + // itself. See the comment in that method for an explanation as to why. + possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) + && used_exactly_once(mir, place.local).unwrap_or(false) + } else { + false + } +} + // Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting // projected type that is a type parameter. Returns `false` if replacing the types would have an // effect on the function signature beyond substituting `new_ty` for `param_ty`. @@ -1437,8 +1506,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data } } -impl Dereferencing { - fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { +impl<'tcx> Dereferencing<'tcx> { + fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 3fac93dcc90c5..fad984d05ca95 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -339,10 +339,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h 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 Some(copy_id) = cx.tcx.lang_items().copy_trait() else { return }; let (ty_adt, ty_subs) = match *ty.kind() { // Unions can't derive clone. ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 1a381f92c0314..6ac85606d9c7c 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -94,9 +94,8 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { } else { path_def_id(cx, expr) }; - let def_id = match uncalled_path.or_else(|| fn_def_id(cx, expr)) { - Some(def_id) => def_id, - None => return, + let Some(def_id) = uncalled_path.or_else(|| fn_def_id(cx, expr)) else { + return }; let conf = match self.disallowed.get(&def_id) { Some(&index) => &self.conf_disallowed[index], diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 9c834cf014485..b44e62435881f 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -65,28 +65,24 @@ declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) { - Some(higher::If { cond, then, r#else }) => (cond, then, r#else), - _ => return, + let Some(higher::If { cond: cond_expr, then: then_expr, r#else: else_expr }) = higher::If::hir(expr) else { + return }; - let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) { - Some(x) => x, - None => return, + let Some((map_ty, contains_expr)) = try_parse_contains(cx, cond_expr) else { + return }; - let then_search = match find_insert_calls(cx, &contains_expr, then_expr) { - Some(x) => x, - None => return, + let Some(then_search) = find_insert_calls(cx, &contains_expr, then_expr) else { + return }; 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 let Some(else_expr) = else_expr { - let else_search = match find_insert_calls(cx, &contains_expr, else_expr) { - Some(search) => search, - None => return, + let Some(else_search) = find_insert_calls(cx, &contains_expr, else_expr) else { + return; }; if then_search.edits.is_empty() && else_search.edits.is_empty() { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 3732410e71e57..7b9786d7e570f 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -213,9 +213,8 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tc if !closure_ty.has_late_bound_regions() { return true; } - let substs = match closure_ty.kind() { - ty::Closure(_, substs) => substs, - _ => return false, + let ty::Closure(_, substs) = closure_ty.kind() else { + return false; }; let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal); cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig) diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 99bef62f81436..32073536b45bb 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred}; -use clippy_utils::macros::{is_format_macro, FormatArgsExpn, FormatParam, FormatParamUsage}; +use clippy_utils::macros::{ + is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage, +}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs}; use if_chain::if_chain; use itertools::Itertools; @@ -13,6 +15,8 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::def_id::DefId; +use rustc_span::edition::Edition::Edition2021; use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol}; declare_clippy_lint! { @@ -111,11 +115,47 @@ declare_clippy_lint! { /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.65.0"] pub UNINLINED_FORMAT_ARGS, - pedantic, + style, "using non-inlined variables in `format!` calls" } -impl_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, UNINLINED_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); +declare_clippy_lint! { + /// ### What it does + /// Detects [formatting parameters] that have no effect on the output of + /// `format!()`, `println!()` or similar macros. + /// + /// ### Why is this bad? + /// Shorter format specifiers are easier to read, it may also indicate that + /// an expected formatting operation such as adding padding isn't happening. + /// + /// ### Example + /// ```rust + /// println!("{:.}", 1.0); + /// + /// println!("not padded: {:5}", format_args!("...")); + /// ``` + /// Use instead: + /// ```rust + /// println!("{}", 1.0); + /// + /// println!("not padded: {}", format_args!("...")); + /// // OR + /// println!("padded: {:5}", format!("...")); + /// ``` + /// + /// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters + #[clippy::version = "1.66.0"] + pub UNUSED_FORMAT_SPECS, + complexity, + "use of a format specifier that has no effect" +} + +impl_lint_pass!(FormatArgs => [ + FORMAT_IN_FORMAT_ARGS, + TO_STRING_IN_FORMAT_ARGS, + UNINLINED_FORMAT_ARGS, + UNUSED_FORMAT_SPECS, +]); pub struct FormatArgs { msrv: Option, @@ -130,27 +170,26 @@ impl FormatArgs { impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let Some(format_args) = FormatArgsExpn::parse(cx, expr); - let expr_expn_data = expr.span.ctxt().outer_expn_data(); - let outermost_expn_data = outermost_expn_data(expr_expn_data); - if let Some(macro_def_id) = outermost_expn_data.macro_def_id; - if is_format_macro(cx, macro_def_id); - if let ExpnKind::Macro(_, name) = outermost_expn_data.kind; - then { - for arg in &format_args.args { - if !arg.format.is_default() { - continue; - } - if is_aliased(&format_args, arg.param.value.hir_id) { - continue; - } - check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); - check_to_string_in_format_args(cx, name, arg.param.value); + if let Some(format_args) = FormatArgsExpn::parse(cx, expr) + && let expr_expn_data = expr.span.ctxt().outer_expn_data() + && let outermost_expn_data = outermost_expn_data(expr_expn_data) + && let Some(macro_def_id) = outermost_expn_data.macro_def_id + && is_format_macro(cx, macro_def_id) + && let ExpnKind::Macro(_, name) = outermost_expn_data.kind + { + for arg in &format_args.args { + check_unused_format_specifier(cx, arg); + if !arg.format.is_default() { + continue; } - if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { - check_uninlined_args(cx, &format_args, outermost_expn_data.call_site); + if is_aliased(&format_args, arg.param.value.hir_id) { + continue; } + check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); + check_to_string_in_format_args(cx, name, arg.param.value); + } + if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { + check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id); } } } @@ -158,10 +197,84 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { extract_msrv_attr!(LateContext); } -fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span) { +fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) { + let param_ty = cx.typeck_results().expr_ty(arg.param.value).peel_refs(); + + if let Count::Implied(Some(mut span)) = arg.format.precision + && !span.is_empty() + { + span_lint_and_then( + cx, + UNUSED_FORMAT_SPECS, + span, + "empty precision specifier has no effect", + |diag| { + if param_ty.is_floating_point() { + diag.note("a precision specifier is not required to format floats"); + } + + if arg.format.is_default() { + // If there's no other specifiers remove the `:` too + span = arg.format_span(); + } + + diag.span_suggestion_verbose(span, "remove the `.`", "", Applicability::MachineApplicable); + }, + ); + } + + if is_type_diagnostic_item(cx, param_ty, sym::Arguments) && !arg.format.is_default_for_trait() { + span_lint_and_then( + cx, + UNUSED_FORMAT_SPECS, + arg.span, + "format specifiers have no effect on `format_args!()`", + |diag| { + let mut suggest_format = |spec, span| { + let message = format!("for the {spec} to apply consider using `format!()`"); + + if let Some(mac_call) = root_macro_call(arg.param.value.span) + && cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id) + && arg.span.eq_ctxt(mac_call.span) + { + diag.span_suggestion( + cx.sess().source_map().span_until_char(mac_call.span, '!'), + message, + "format", + Applicability::MaybeIncorrect, + ); + } else if let Some(span) = span { + diag.span_help(span, message); + } + }; + + if !arg.format.width.is_implied() { + suggest_format("width", arg.format.width.span()); + } + + if !arg.format.precision.is_implied() { + suggest_format("precision", arg.format.precision.span()); + } + + diag.span_suggestion_verbose( + arg.format_span(), + "if the current behavior is intentional, remove the format specifiers", + "", + Applicability::MaybeIncorrect, + ); + }, + ); + } +} + +fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) { if args.format_string.span.from_expansion() { return; } + if call_site.edition() < Edition2021 && is_panic(cx, def_id) { + // panic! before 2021 edition considers a single string argument as non-format + return; + } let mut fixes = Vec::new(); // If any of the arguments are referenced by an index number, @@ -248,7 +361,7 @@ fn check_format_in_format_args( fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) { if_chain! { if !value.span.from_expansion(); - if let ExprKind::MethodCall(_, receiver, [], _) = value.kind; + if let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind; if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id); if is_diag_trait_item(cx, method_def_id, sym::ToString); let receiver_ty = cx.typeck_results().expr_ty(receiver); @@ -264,7 +377,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex span_lint_and_sugg( cx, TO_STRING_IN_FORMAT_ARGS, - value.span.with_lo(receiver.span.hi()), + to_string_span.with_lo(receiver.span.hi()), &format!( "`to_string` applied to a type that implements `Display` in `{name}!` args" ), diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 5d25c1d06341f..95eda4ea88275 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,11 +1,19 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{meets_msrv, msrvs}; -use if_chain::if_chain; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::span_is_local; +use clippy_utils::source::snippet_opt; +use clippy_utils::{meets_msrv, msrvs, path_def_id}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_path, Visitor}; +use rustc_hir::{ + GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, PathSegment, Ty, + TyKind, +}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::sym; +use rustc_span::symbol::{kw, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -54,28 +62,152 @@ impl FromOverInto { impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { return; } - if_chain! { - if let hir::ItemKind::Impl{ .. } = &item.kind; - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.def_id); - if cx.tcx.is_diagnostic_item(sym::Into, impl_trait_ref.def_id); - - then { - span_lint_and_help( - cx, - FROM_OVER_INTO, - 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, - &format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()), - ); - } + if let ItemKind::Impl(Impl { + of_trait: Some(hir_trait_ref), + self_ty, + items: [impl_item_ref], + .. + }) = item.kind + && let Some(into_trait_seg) = hir_trait_ref.path.segments.last() + // `impl Into for self_ty` + && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args + && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.def_id) + && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) + { + span_lint_and_then( + cx, + FROM_OVER_INTO, + 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", + |diag| { + // If the target type is likely foreign mention the orphan rules as it's a common source of confusion + if path_def_id(cx, target_ty.peel_refs()).map_or(true, |id| !id.is_local()) { + diag.help( + "`impl From for Foreign` is allowed by the orphan rules, for more information see\n\ + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence" + ); + } + + let message = format!("replace the `Into` implentation with `From<{}>`", middle_trait_ref.self_ty()); + if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) { + diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); + } else { + diag.help(message); + } + }, + ); } } extract_msrv_attr!(LateContext); } + +/// Finds the occurences of `Self` and `self` +struct SelfFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + /// Occurences of `Self` + upper: Vec, + /// Occurences of `self` + lower: Vec, + /// If any of the `self`/`Self` usages were from an expansion, or the body contained a binding + /// already named `val` + invalid: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) { + for segment in path.segments { + match segment.ident.name { + kw::SelfLower => self.lower.push(segment.ident.span), + kw::SelfUpper => self.upper.push(segment.ident.span), + _ => continue, + } + } + + self.invalid |= path.span.from_expansion(); + if !self.invalid { + walk_path(self, path); + } + } + + fn visit_name(&mut self, name: Symbol) { + if name == sym::val { + self.invalid = true; + } + } +} + +fn convert_to_from( + cx: &LateContext<'_>, + into_trait_seg: &PathSegment<'_>, + target_ty: &Ty<'_>, + self_ty: &Ty<'_>, + impl_item_ref: &ImplItemRef, +) -> Option> { + let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id); + let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else { return None }; + let body = cx.tcx.hir().body(body_id); + let [input] = body.params else { return None }; + let PatKind::Binding(.., self_ident, None) = input.pat.kind else { return None }; + + let from = snippet_opt(cx, self_ty.span)?; + let into = snippet_opt(cx, target_ty.span)?; + + let mut suggestions = vec![ + // impl Into for U -> impl From for U + // ~~~~ ~~~~ + (into_trait_seg.ident.span, String::from("From")), + // impl Into for U -> impl Into for U + // ~ ~ + (target_ty.span, from.clone()), + // impl Into for U -> impl Into for T + // ~ ~ + (self_ty.span, into), + // fn into(self) -> T -> fn from(self) -> T + // ~~~~ ~~~~ + (impl_item.ident.span, String::from("from")), + // fn into([mut] self) -> T -> fn into([mut] v: T) -> T + // ~~~~ ~~~~ + (self_ident.span, format!("val: {from}")), + // fn into(self) -> T -> fn into(self) -> Self + // ~ ~~~~ + (sig.decl.output.span(), String::from("Self")), + ]; + + let mut finder = SelfFinder { + cx, + upper: Vec::new(), + lower: Vec::new(), + invalid: false, + }; + finder.visit_expr(body.value); + + if finder.invalid { + return None; + } + + // don't try to replace e.g. `Self::default()` with `&[T]::default()` + if !finder.upper.is_empty() && !matches!(self_ty.kind, TyKind::Path(_)) { + return None; + } + + for span in finder.upper { + suggestions.push((span, from.clone())); + } + for span in finder.lower { + suggestions.push((span, String::from("val"))); + } + + Some(suggestions) +} diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index d263804f32cf4..3064b6c9d22f8 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -7,14 +7,14 @@ use rustc_middle::{ lint::in_external_macro, ty::{self, Ty}, }; -use rustc_span::{sym, Span}; +use rustc_span::{sym, Span, Symbol}; use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{match_def_path, return_ty, trait_ref_of_method}; +use clippy_utils::{return_ty, trait_ref_of_method}; use core::ops::ControlFlow; @@ -181,7 +181,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) } } -static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; +static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc]; fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool { match *ty.kind() { @@ -189,7 +189,9 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, 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)) + || KNOWN_WRAPPER_TYS + .iter() + .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did())) && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) }, ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, span, tys)), diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index f83f8b40f94b7..bd473ac7e51b0 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -22,9 +22,8 @@ pub(super) fn check_fn( return; } - let code_snippet = match snippet_opt(cx, body.value.span) { - Some(s) => s, - _ => return, + let Some(code_snippet) = snippet_opt(cx, body.value.span) else { + return }; let mut line_count: u64 = 0; let mut in_comment = false; diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 48edbf6ae576c..29d59c26d92c4 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.44.0"] pub IMPLICIT_SATURATING_SUB, - pedantic, + style, "Perform saturating subtraction instead of implicitly checking lower bound of data type" } diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 36e03e50a8e4f..0ef77e03de906 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -145,9 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { 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 - } else { + let Some((rel, normalized_lhs, normalized_rhs)) = normalized else { return; }; diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index eb13d0869c037..8ed7e4bb196cd 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -124,9 +124,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { } if let ItemKind::Enum(ref def, _) = item.kind { let ty = cx.tcx.type_of(item.def_id); - let (adt, subst) = match ty.kind() { - Adt(adt, subst) => (adt, subst), - _ => panic!("already checked whether this is an enum"), + let Adt(adt, subst) = ty.kind() else { + panic!("already checked whether this is an enum") }; if adt.variants().len() <= 1 { return; diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 176787497ebf2..b7798b1c1d749 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_must_use_ty, match_type}; +use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, match_type}; use clippy_utils::{is_must_use_func_call, paths}; use if_chain::if_chain; use rustc_hir::{Local, PatKind}; @@ -7,6 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; 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::{sym, Symbol}; declare_clippy_lint! { /// ### What it does @@ -99,10 +100,9 @@ declare_clippy_lint! { declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]); -const SYNC_GUARD_PATHS: [&[&str]; 6] = [ - &paths::MUTEX_GUARD, - &paths::RWLOCK_READ_GUARD, - &paths::RWLOCK_WRITE_GUARD, +const SYNC_GUARD_SYMS: [Symbol; 3] = [sym::MutexGuard, sym::RwLockReadGuard, sym::RwLockWriteGuard]; + +const SYNC_GUARD_PATHS: [&[&str]; 3] = [ &paths::PARKING_LOT_MUTEX_GUARD, &paths::PARKING_LOT_RWLOCK_READ_GUARD, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD, @@ -121,7 +121,10 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { let init_ty = cx.typeck_results().expr_ty(init); let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => { - SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) + SYNC_GUARD_SYMS + .iter() + .any(|&sym| is_type_diagnostic_item(cx, inner_ty, sym)) + || SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) }, GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, @@ -134,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "non-binding let on a synchronization lock", None, "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`" + binding or dropping explicitly with `std::mem::drop`", ); } else if init_ty.needs_drop(cx.tcx, cx.param_env) { span_lint_and_help( @@ -144,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "non-binding `let` on a type that implements `Drop`", None, "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`" + 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( @@ -153,7 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { local.span, "non-binding let on an expression with `#[must_use]` type", None, - "consider explicitly using expression value" + "consider explicitly using expression value", ); } else if is_must_use_func_call(cx, init) { span_lint_and_help( @@ -162,7 +165,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { local.span, "non-binding let on a result of a `#[must_use]` function", None, - "consider explicitly using function result" + "consider explicitly using function result", ); } } diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index fe1f0b56646cd..f5ad52ba1892a 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -25,6 +25,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), + LintId::of(casts::CAST_NAN_TO_INT), LintId::of(casts::CAST_REF_TO_MUT), LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), @@ -71,6 +72,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(format::USELESS_FORMAT), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), + LintId::of(format_args::UNUSED_FORMAT_SPECS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), LintId::of(formatting::POSSIBLE_MISSING_COMMA), @@ -87,6 +90,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(functions::TOO_MANY_ARGUMENTS), LintId::of(if_let_mutex::IF_LET_MUTEX), LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), + LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(infinite_iter::INFINITE_ITER), LintId::of(inherent_to_string::INHERENT_TO_STRING), @@ -136,6 +140,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(match_result_ok::MATCH_RESULT_OK), LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MANUAL_FILTER), LintId::of(matches::MANUAL_MAP), LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index a58d066fa6b67..8be9dc4baf193 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -13,6 +13,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(double_parens::DOUBLE_PARENS), LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(format::USELESS_FORMAT), + LintId::of(format_args::UNUSED_FORMAT_SPECS), LintId::of(functions::TOO_MANY_ARGUMENTS), LintId::of(int_plus_one::INT_PLUS_ONE), LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), @@ -27,6 +28,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(manual_strip::MANUAL_STRIP), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(matches::MANUAL_FILTER), LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_SINGLE_BINDING), diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs index 71dfdab369b97..40c94c6e8d33d 100644 --- a/clippy_lints/src/lib.register_internal.rs +++ b/clippy_lints/src/lib.register_internal.rs @@ -3,20 +3,20 @@ // Manual edits will be overwritten. store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ - LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL), - LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), - LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS), - LintId::of(utils::internal_lints::DEFAULT_DEPRECATION_REASON), - 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_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::INVALID_PATHS), - LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), - LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL), - LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), - LintId::of(utils::internal_lints::PRODUCE_ICE), - LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH), - LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), + LintId::of(utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL), + LintId::of(utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS), + LintId::of(utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS), + LintId::of(utils::internal_lints::if_chain_style::IF_CHAIN_STYLE), + LintId::of(utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL), + LintId::of(utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR), + LintId::of(utils::internal_lints::invalid_paths::INVALID_PATHS), + LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON), + LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT), + LintId::of(utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE), + LintId::of(utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS), + LintId::of(utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE), + LintId::of(utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL), + LintId::of(utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA), + LintId::of(utils::internal_lints::produce_ice::PRODUCE_ICE), + LintId::of(utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH), ]) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 306cb6a61c943..800e3a8767133 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -4,37 +4,37 @@ store.register_lints(&[ #[cfg(feature = "internal")] - utils::internal_lints::CLIPPY_LINTS_INTERNAL, + utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL, #[cfg(feature = "internal")] - utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, #[cfg(feature = "internal")] - utils::internal_lints::COMPILER_LINT_FUNCTIONS, + utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS, #[cfg(feature = "internal")] - utils::internal_lints::DEFAULT_DEPRECATION_REASON, + utils::internal_lints::if_chain_style::IF_CHAIN_STYLE, #[cfg(feature = "internal")] - utils::internal_lints::DEFAULT_LINT, + utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL, #[cfg(feature = "internal")] - utils::internal_lints::IF_CHAIN_STYLE, + utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR, #[cfg(feature = "internal")] - utils::internal_lints::INTERNING_DEFINED_SYMBOL, + utils::internal_lints::invalid_paths::INVALID_PATHS, #[cfg(feature = "internal")] - utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE, + utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON, #[cfg(feature = "internal")] - utils::internal_lints::INVALID_PATHS, + utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT, #[cfg(feature = "internal")] - utils::internal_lints::LINT_WITHOUT_LINT_PASS, + utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, #[cfg(feature = "internal")] - utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, + utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal")] - utils::internal_lints::MISSING_MSRV_ATTR_IMPL, + utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE, #[cfg(feature = "internal")] - utils::internal_lints::OUTER_EXPN_EXPN_DATA, + utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL, #[cfg(feature = "internal")] - utils::internal_lints::PRODUCE_ICE, + utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA, #[cfg(feature = "internal")] - utils::internal_lints::UNNECESSARY_DEF_PATH, + utils::internal_lints::produce_ice::PRODUCE_ICE, #[cfg(feature = "internal")] - utils::internal_lints::UNNECESSARY_SYMBOL_STR, + utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH, almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, approx_const::APPROX_CONSTANT, as_conversions::AS_CONVERSIONS, @@ -66,12 +66,14 @@ store.register_lints(&[ cargo::NEGATIVE_FEATURE_NAMES, cargo::REDUNDANT_FEATURE_NAMES, cargo::WILDCARD_DEPENDENCIES, + casts::AS_PTR_CAST_MUT, casts::AS_UNDERSCORE, casts::BORROW_AS_PTR, casts::CAST_ABS_TO_UNSIGNED, casts::CAST_ENUM_CONSTRUCTOR, casts::CAST_ENUM_TRUNCATION, casts::CAST_LOSSLESS, + casts::CAST_NAN_TO_INT, casts::CAST_POSSIBLE_TRUNCATION, casts::CAST_POSSIBLE_WRAP, casts::CAST_PRECISION_LOSS, @@ -162,6 +164,7 @@ store.register_lints(&[ format_args::FORMAT_IN_FORMAT_ARGS, format_args::TO_STRING_IN_FORMAT_ARGS, format_args::UNINLINED_FORMAT_ARGS, + format_args::UNUSED_FORMAT_SPECS, format_impl::PRINT_IN_FORMAT_IMPL, format_impl::RECURSIVE_FORMAT_IMPL, format_push_string::FORMAT_PUSH_STRING, @@ -258,6 +261,7 @@ store.register_lints(&[ match_result_ok::MATCH_RESULT_OK, matches::COLLAPSIBLE_MATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, + matches::MANUAL_FILTER, matches::MANUAL_MAP, matches::MANUAL_UNWRAP_OR, matches::MATCH_AS_REF, @@ -403,6 +407,7 @@ store.register_lints(&[ missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, + missing_trait_methods::MISSING_TRAIT_METHODS, mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION, mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION, module_style::MOD_MODULE_FILES, @@ -475,6 +480,7 @@ store.register_lints(&[ panic_unimplemented::TODO, panic_unimplemented::UNIMPLEMENTED, panic_unimplemented::UNREACHABLE, + partial_pub_fields::PARTIAL_PUB_FIELDS, partialeq_ne_impl::PARTIALEQ_NE_IMPL, partialeq_to_none::PARTIALEQ_TO_NONE, pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index e0b4639af53e6..65616d28d8f10 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -4,6 +4,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), + LintId::of(casts::AS_PTR_CAST_MUT), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(copies::BRANCHES_SHARING_CODE), LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index bc2f0beb358a5..060d3bdc7c6fa 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -29,12 +29,10 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ 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(format_args::UNINLINED_FORMAT_ARGS), LintId::of(functions::MUST_USE_CANDIDATE), LintId::of(functions::TOO_MANY_LINES), LintId::of(if_not_else::IF_NOT_ELSE), LintId::of(implicit_hasher::IMPLICIT_HASHER), - LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(infinite_iter::MAYBE_INFINITE_ITER), LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 6eb9b3d3b9b7a..f62d57af5b47f 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -47,6 +47,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve 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(missing_trait_methods::MISSING_TRAIT_METHODS), LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION), LintId::of(module_style::MOD_MODULE_FILES), LintId::of(module_style::SELF_NAMED_MODULE_FILES), @@ -61,6 +62,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(panic_unimplemented::TODO), LintId::of(panic_unimplemented::UNIMPLEMENTED), LintId::of(panic_unimplemented::UNREACHABLE), + LintId::of(partial_pub_fields::PARTIAL_PUB_FIELDS), LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), LintId::of(pub_use::PUB_USE), LintId::of(redundant_slicing::DEREF_BY_SLICING), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 8e1390167dc81..6894d69e928a7 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -25,12 +25,14 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(enum_variants::MODULE_INCEPTION), LintId::of(eta_reduction::REDUNDANT_CLOSURE), LintId::of(float_literal::EXCESSIVE_PRECISION), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), 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(implicit_saturating_add::IMPLICIT_SATURATING_ADD), + LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(len_zero::COMPARISON_TO_EMPTY), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index d6d95c95c85d2..b70c4bb73e57d 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -11,6 +11,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), + LintId::of(casts::CAST_NAN_TO_INT), LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(drop_forget_ref::DROP_NON_DROP), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 89ffca8128a9e..1307096b28d7b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -39,7 +39,6 @@ extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; -extern crate rustc_mir_dataflow; extern crate rustc_parse; extern crate rustc_session; extern crate rustc_span; @@ -291,6 +290,7 @@ mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; mod missing_inline; +mod missing_trait_methods; mod mixed_read_write_in_expression; mod module_style; mod multi_assignments; @@ -326,6 +326,7 @@ mod option_if_let_else; mod overflow_check_conditional; mod panic_in_result_fn; mod panic_unimplemented; +mod partial_pub_fields; mod partialeq_ne_impl; mod partialeq_to_none; mod pass_by_ref_or_value; @@ -419,7 +420,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None @@ -435,7 +436,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { .and_then(|v| parse_msrv(&v, None, None)); let clippy_msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None @@ -446,7 +447,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { if let Some(clippy_msrv) = clippy_msrv { // if both files have an msrv, let's compare them and emit a warning if they differ if clippy_msrv != cargo_msrv { - sess.warn(&format!( + sess.warn(format!( "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" )); } @@ -475,7 +476,7 @@ pub fn read_conf(sess: &Session) -> Conf { let TryConf { conf, errors, warnings } = 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.err(&format!( + sess.err(format!( "error reading Clippy's configuration file `{}`: {}", file_name.display(), format_error(error) @@ -483,7 +484,7 @@ pub fn read_conf(sess: &Session) -> Conf { } for warning in warnings { - sess.struct_warn(&format!( + sess.struct_warn(format!( "error reading Clippy's configuration file `{}`: {}", file_name.display(), format_error(warning) @@ -530,17 +531,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // all the internal lints #[cfg(feature = "internal")] { - store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); - store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); - store.register_late_pass(|_| Box::new(utils::internal_lints::CollapsibleCalls)); - store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new())); - store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle)); - store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(utils::internal_lints::UnnecessaryDefPath)); - store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass)); - store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl)); + store.register_early_pass(|| Box::new(utils::internal_lints::clippy_lints_internal::ClippyLintsInternal)); + store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce)); + store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls)); + store.register_late_pass(|_| { + Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new()) + }); + store.register_late_pass(|_| Box::new(utils::internal_lints::if_chain_style::IfChainStyle)); + store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths)); + store.register_late_pass(|_| { + Box::::default() + }); + store.register_late_pass(|_| { + Box::::default() + }); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass)); + store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl)); } let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone(); @@ -910,6 +917,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)); store.register_late_pass(|_| Box::new(box_default::BoxDefault)); store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); + store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields)); + store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 00cfc6d49f19a..27ba27202bf7e 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq}; +use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -263,7 +263,8 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { match res { Res::Local(hir_id) => { let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); - let extent = self.cx + let extent = self + .cx .tcx .region_scope_tree(parent_def_id) .var_scope(hir_id.local_id) @@ -274,11 +275,12 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), ); } else { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); + self.indexed_indirectly + .insert(seqvar.segments[0].ident.name, Some(extent)); } - return false; // no need to walk further *on the variable* - } - Res::Def(DefKind::Static (_)| DefKind::Const, ..) => { + return false; // no need to walk further *on the variable* + }, + Res::Def(DefKind::Static(_) | DefKind::Const, ..) => { if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, @@ -287,8 +289,8 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { } else { self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); } - return false; // no need to walk further *on the variable* - } + return false; // no need to walk further *on the variable* + }, _ => (), } } @@ -302,17 +304,26 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { if_chain! { // a range index op if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &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 let Some(trait_id) = self + .cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .and_then(|def_id| self.cx.tcx.trait_of_item(def_id)); + if (meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id)) + || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id)); if !self.check(args_1, args_0, expr); - then { return } + then { + return; + } } if_chain! { // an index op if let ExprKind::Index(seqexpr, idx) = expr.kind; if !self.check(idx, seqexpr, expr); - then { return } + then { + return; + } } if_chain! { diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 153f97e4e66c8..55989f8a44650 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -331,9 +331,8 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & } if let Some(e) = get_enclosing_loop_or_multi_call_closure(cx, loop_expr) { - let local_id = match iter_expr.path { - Res::Local(id) => id, - _ => return true, + let Res::Local(local_id) = iter_expr.path else { + return true }; let mut v = NestedLoopVisitor { cx, diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 9a0a26c0991b3..090f9f8ff73cf 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::match_function_call; -use clippy_utils::paths::FUTURE_FROM_GENERATOR; +use clippy_utils::match_function_call_with_def_id; use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -140,9 +139,9 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t if args.bindings.len() == 1; let binding = &args.bindings[0]; if binding.ident.name == sym::Output; - if let TypeBindingKind::Equality{term: Term::Ty(output)} = binding.kind; + if let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind; then { - return Some(output) + return Some(output); } } @@ -175,9 +174,16 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) 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 let Some(args) = cx + .tcx + .lang_items() + .from_generator_fn() + .and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id)); if args.len() == 1; - if let Expr{kind: ExprKind::Closure(&Closure { body, .. }), ..} = args[0]; + if let Expr { + kind: ExprKind::Closure(&Closure { body, .. }), + .. + } = args[0]; let closure_body = cx.tcx.hir().body(body); if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block)); then { diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index ece4df95505ce..02dc8755dd61c 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -12,9 +12,9 @@ use std::ops::Deref; use clippy_utils::{ diagnostics::{span_lint_and_then, span_lint_hir_and_then}, - eq_expr_value, get_trait_def_id, + eq_expr_value, higher::If, - is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, paths, peel_blocks, + is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt, sugg::Sugg, ty::implements_trait, @@ -190,7 +190,11 @@ impl TypeClampability { fn is_clampable<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option { if ty.is_floating_point() { Some(TypeClampability::Float) - } else if get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) { + } else if cx + .tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { Some(TypeClampability::Ord) } else { None diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index fd14d868df348..33a052c41a38a 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, @@ -63,7 +64,8 @@ fn check_arm<'tcx>( if !pat_contains_or(inner_then_pat); // the binding must come from the pattern of the containing match arm // .... => match { .. } - if let Some(binding_span) = find_pat_binding(outer_pat, binding_id); + if let (Some(binding_span), is_innermost_parent_pat_struct) + = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id); // the "else" branches must be equal if match (outer_else_body, inner_else_body) { (None, None) => true, @@ -88,6 +90,13 @@ fn check_arm<'tcx>( if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" }, if outer_is_match { "match" } else { "if let" }, ); + // collapsing patterns need an explicit field name in struct pattern matching + // ex: Struct {x: Some(1)} + let replace_msg = if is_innermost_parent_pat_struct { + format!(", prefixed by {}:", snippet(cx, binding_span, "their field name")) + } else { + String::new() + }; span_lint_and_then( cx, COLLAPSIBLE_MATCH, @@ -96,7 +105,7 @@ fn check_arm<'tcx>( |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding"); - help_span.push_span_label(inner_then_pat.span, "with this pattern"); + help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); @@ -117,8 +126,9 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } } -fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { +fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option, bool) { let mut span = None; + let mut is_innermost_parent_pat_struct = false; pat.walk_short(|p| match &p.kind { // ignore OR patterns PatKind::Or(_) => false, @@ -129,9 +139,12 @@ fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { } !found }, - _ => true, + _ => { + is_innermost_parent_pat_struct = matches!(p.kind, PatKind::Struct(..)); + true + }, }); - span + (span, is_innermost_parent_pat_struct) } fn pat_contains_or(pat: &Pat<'_>) -> bool { diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs new file mode 100644 index 0000000000000..66ba1f6f9c550 --- /dev/null +++ b/clippy_lints/src/matches/manual_filter.rs @@ -0,0 +1,153 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::contains_unsafe_block; +use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; + +use rustc_hir::LangItem::OptionSome; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +use super::manual_utils::{check_with, SomeExpr}; +use super::MANUAL_FILTER; + +// Function called on the of `[&+]Some((ref | ref mut) x) => ` +// Need to check if it's of the form `=if {} else {}` +// AND that only one `then/else_expr` resolves to `Some(x)` while the other resolves to `None` +// return the `cond` expression if so. +fn get_cond_expr<'tcx>( + cx: &LateContext<'tcx>, + pat: &Pat<'_>, + expr: &'tcx Expr<'_>, + ctxt: SyntaxContext, +) -> Option> { + if_chain! { + if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr); + if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; + if let PatKind::Binding(_,target, ..) = pat.kind; + if let (then_visitor, else_visitor) + = (is_some_expr(cx, target, ctxt, then_expr), + is_some_expr(cx, target, ctxt, else_expr)); + if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None` + then { + return Some(SomeExpr { + expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), + needs_unsafe_block: contains_unsafe_block(cx, expr), + needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond + }) + } + }; + None +} + +fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { + // we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's + // checked by `contains_unsafe_block` + if let ExprKind::Block(block, None) = expr.kind { + if block.stmts.is_empty() { + return block.expr; + } + }; + None +} + +fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { + peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr) +} + +// function called for each expression: +// Some(x) => if { +// +// } else { +// +// } +// Returns true if resolves to `Some(x)`, `false` otherwise +fn is_some_expr<'tcx>(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &'tcx Expr<'_>) -> bool { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { + // there can be not statements in the block as they would be removed when switching to `.filter` + if let ExprKind::Call(callee, [arg]) = inner_expr.kind { + return ctxt == expr.span.ctxt() + && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) + && path_to_local_id(arg, target); + } + }; + false +} + +// given the closure: `|| ` +// returns `|&| ` +fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String { + if has_copy_trait { + let mut with_ampersand = body_str; + with_ampersand.insert(1, '&'); + with_ampersand + } else { + body_str + } +} + +pub(super) fn check_match<'tcx>( + cx: &LateContext<'tcx>, + scrutinee: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], + expr: &'tcx Expr<'_>, +) { + let ty = cx.typeck_results().expr_ty(expr); + if is_type_diagnostic_item(cx, ty, sym::Option) + && let [first_arm, second_arm] = arms + && first_arm.guard.is_none() + && second_arm.guard.is_none() + { + check(cx, expr, scrutinee, first_arm.pat, first_arm.body, Some(second_arm.pat), second_arm.body); + } +} + +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + let_pat: &'tcx Pat<'_>, + let_expr: &'tcx Expr<'_>, + then_expr: &'tcx Expr<'_>, + else_expr: &'tcx Expr<'_>, +) { + check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); +} + +fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, +) { + if let Some(sugg_info) = check_with( + cx, + expr, + scrutinee, + then_pat, + then_body, + else_pat, + else_body, + get_cond_expr, + ) { + let body_str = add_ampersand_if_copy(sugg_info.body_str, sugg_info.scrutinee_impl_copy); + span_lint_and_sugg( + cx, + MANUAL_FILTER, + expr.span, + "manual implementation of `Option::filter`", + "try this", + if sugg_info.needs_brackets { + format!( + "{{ {}{}.filter({body_str}) }}", + sugg_info.scrutinee_str, sugg_info.as_ref_str + ) + } else { + format!("{}{}.filter({body_str})", sugg_info.scrutinee_str, sugg_info.as_ref_str) + }, + sugg_info.app, + ); + } +} diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index 76f5e1c941c7a..aaba239677fff 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -1,22 +1,13 @@ -use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use super::manual_utils::{check_with, SomeExpr}; +use super::MANUAL_MAP; 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, type_is_unsafe_function}; -use clippy_utils::{ - can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, - peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, -}; -use rustc_ast::util::parser::PREC_POSTFIX; -use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{ - def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, - QPath, UnsafeSource, -}; -use rustc_lint::LateContext; -use rustc_span::{sym, SyntaxContext}; -use super::MANUAL_MAP; +use clippy_utils::{is_res_lang_ctor, path_res}; + +use rustc_hir::LangItem::OptionSome; +use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_span::SyntaxContext; pub(super) fn check_match<'tcx>( cx: &LateContext<'tcx>, @@ -43,7 +34,6 @@ pub(super) fn check_if_let<'tcx>( check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); } -#[expect(clippy::too_many_lines)] fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -53,254 +43,74 @@ fn check<'tcx>( else_pat: Option<&'tcx Pat<'_>>, else_body: &'tcx Expr<'_>, ) { - 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) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) - { - return; - } - - let expr_ctxt = expr.span.ctxt(); - let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( - try_parse_pattern(cx, then_pat, expr_ctxt), - else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), + if let Some(sugg_info) = check_with( + cx, + expr, + scrutinee, + then_pat, + then_body, + else_pat, + else_body, + get_some_expr, ) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, true) - }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, false) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, true) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { - (then_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, false, expr_ctxt) { - Some(expr) => expr, - None => return, - }; - - // These two lints will go back and forth with each other. - if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit - && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) - { - return; - } - - // `map` won't perform any adjustments. - if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { - 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_some(ty_mutability)); - - let as_ref_str = match binding_ref { - Some(Mutability::Mut) => ".as_mut()", - Some(Mutability::Not) => ".as_ref()", - None => "", - }; - - match can_move_expr_to_closure(cx, some_expr.expr) { - Some(captures) => { - // Check if captures the closure will need conflict with borrows made in the scrutinee. - // TODO: check all the references made in the scrutinee expression. This will require interacting - // with the borrow checker. Currently only `[.]*` is checked for. - if let Some(binding_ref_mutability) = binding_ref { - let e = peel_hir_expr_while(scrutinee, |e| match e.kind { - ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { - match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, - Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { - return; - }, - Some(CaptureKind::Ref(Mutability::Not)) | None => (), - } - } - } - }, - None => return, - }; - - let mut app = Applicability::MachineApplicable; - - // 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 = 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, id, some_binding, None) = some_pat.kind { - if_chain! { - if !some_expr.needs_unsafe_block; - if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); - if func.span.ctxt() == some_expr.expr.span.ctxt(); - then { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + span_lint_and_sugg( + cx, + MANUAL_MAP, + expr.span, + "manual implementation of `Option::map`", + "try this", + if sugg_info.needs_brackets { + format!( + "{{ {}{}.map({}) }}", + sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str + ) } else { - if path_to_local_id(some_expr.expr, id) - && !is_lint_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::MUT) { - "mut " - } else { - "" - }; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{annotation}{some_binding}| unsafe {{ {expr_snip} }}") - } else { - format!("|{annotation}{some_binding}| {expr_snip}") - } - } - } - } else if !is_wild_none && explicit_ref.is_none() { - // TODO: handle explicit reference annotations. - let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{pat_snip}| unsafe {{ {expr_snip} }}") - } else { - format!("|{pat_snip}| {expr_snip}") - } - } 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", - if else_pat.is_none() && is_else_clause(cx.tcx, expr) { - format!("{{ {scrutinee_str}{as_ref_str}.map({body_str}) }}") - } else { - format!("{scrutinee_str}{as_ref_str}.map({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<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - match expr.kind { - ExprKind::Call(func, [arg]) - if path_to_local_id(arg, binding) - && cx.typeck_results().expr_adjustments(arg).is_empty() - && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => - { - 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, - }, -} - -struct SomeExpr<'tcx> { - expr: &'tcx Expr<'tcx>, - needs_unsafe_block: bool, -} - -// Try to parse into a recognized `Option` pattern. -// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { - fn f<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - ref_count: usize, - ctxt: SyntaxContext, - ) -> Option> { - match pat.kind { - PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { - Some(OptionPat::None) - }, - PatKind::TupleStruct(ref qpath, [pattern], _) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => - { - Some(OptionPat::Some { pattern, ref_count }) + format!( + "{}{}.map({})", + sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str + ) }, - _ => None, - } + sugg_info.app, + ); } - f(cx, pat, 0, ctxt) } // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. fn get_some_expr<'tcx>( cx: &LateContext<'tcx>, + _: &'tcx Pat<'_>, expr: &'tcx Expr<'_>, - needs_unsafe_block: bool, ctxt: SyntaxContext, ) -> Option> { - // TODO: Allow more complex expressions. - match expr.kind { - ExprKind::Call(callee, [arg]) - if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => - { - Some(SomeExpr { - expr: arg, - needs_unsafe_block, - }) - }, - ExprKind::Block( - Block { - stmts: [], - expr: Some(expr), - rules, - .. + fn get_some_expr_internal<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + needs_unsafe_block: bool, + ctxt: SyntaxContext, + ) -> Option> { + // TODO: Allow more complex expressions. + match expr.kind { + ExprKind::Call(callee, [arg]) + if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => + { + Some(SomeExpr::new_no_negated(arg, needs_unsafe_block)) }, - _, - ) => get_some_expr( - cx, - expr, - needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), - ctxt, - ), - _ => None, + ExprKind::Block( + Block { + stmts: [], + expr: Some(expr), + rules, + .. + }, + _, + ) => get_some_expr_internal( + cx, + expr, + needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + ctxt, + ), + _ => None, + } } -} - -// Checks for the `None` value. -fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) + get_some_expr_internal(cx, expr, false, ctxt) } diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs new file mode 100644 index 0000000000000..5b7644a538323 --- /dev/null +++ b/clippy_lints/src/matches/manual_utils.rs @@ -0,0 +1,277 @@ +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::{ + can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, + peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, sugg::Sugg, CaptureKind, +}; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_lines)] +pub(super) fn check_with<'tcx, F>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, + get_some_expr_fn: F, +) -> Option> +where + F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, +{ + 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) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) + { + return None; + } + + let expr_ctxt = expr.span.ctxt(); + let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( + try_parse_pattern(cx, then_pat, expr_ctxt), + else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), + ) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, false) + }, + _ => return None, + }; + + // Top level or patterns aren't allowed in closures. + if matches!(some_pat.kind, PatKind::Or(_)) { + return None; + } + + let Some(some_expr) = get_some_expr_fn(cx, some_pat, some_expr, expr_ctxt) else { + return None; + }; + + // These two lints will go back and forth with each other. + if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit + && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) + { + return None; + } + + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { + return None; + } + + // 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_some(ty_mutability)); + + let as_ref_str = match binding_ref { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", + }; + + match can_move_expr_to_closure(cx, some_expr.expr) { + Some(captures) => { + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if let Some(binding_ref_mutability) = binding_ref { + let e = peel_hir_expr_while(scrutinee, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match captures.get(l) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, + Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { + return None; + }, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } + } + } + }, + None => return None, + }; + + let mut app = Applicability::MachineApplicable; + + // 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 = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { + format!("({scrutinee_str})") + } else { + scrutinee_str.into() + }; + + let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app); + let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { + if_chain! { + if !some_expr.needs_unsafe_block; + if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); + if func.span.ctxt() == some_expr.expr.span.ctxt(); + then { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + } else { + if path_to_local_id(some_expr.expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return None; + } + + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::MUT) { + "mut " + } else { + "" + }; + + if some_expr.needs_unsafe_block { + format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") + } else { + format!("|{annotation}{some_binding}| {closure_expr_snip}") + } + } + } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { + format!("|{pat_snip}| unsafe {{ {closure_expr_snip} }}") + } else { + format!("|{pat_snip}| {closure_expr_snip}") + } + } else { + // Refutable bindings and mixed reference annotations can't be handled by `map`. + return None; + }; + + // relies on the fact that Option: Copy where T: copy + let scrutinee_impl_copy = is_copy(cx, scrutinee_ty); + + Some(SuggInfo { + needs_brackets: else_pat.is_none() && is_else_clause(cx.tcx, expr), + scrutinee_impl_copy, + scrutinee_str, + as_ref_str, + body_str, + app, + }) +} + +pub struct SuggInfo<'a> { + pub needs_brackets: bool, + pub scrutinee_impl_copy: bool, + pub scrutinee_str: String, + pub as_ref_str: &'a str, + pub body_str: String, + pub app: Applicability, +} + +// 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<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + match expr.kind { + ExprKind::Call(func, [arg]) + if path_to_local_id(arg, binding) + && cx.typeck_results().expr_adjustments(arg).is_empty() + && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => + { + Some(func) + }, + _ => None, + } +} + +#[derive(Debug)] +pub(super) 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, + }, +} + +pub(super) struct SomeExpr<'tcx> { + pub expr: &'tcx Expr<'tcx>, + pub needs_unsafe_block: bool, + pub needs_negated: bool, // for `manual_filter` lint +} + +impl<'tcx> SomeExpr<'tcx> { + pub fn new_no_negated(expr: &'tcx Expr<'tcx>, needs_unsafe_block: bool) -> Self { + Self { + expr, + needs_unsafe_block, + needs_negated: false, + } + } + + pub fn to_snippet_with_context( + &self, + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + app: &mut Applicability, + ) -> Sugg<'tcx> { + let sugg = Sugg::hir_with_context(cx, self.expr, ctxt, "..", app); + if self.needs_negated { !sugg } else { sugg } + } +} + +// Try to parse into a recognized `Option` pattern. +// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. +pub(super) fn try_parse_pattern<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ctxt: SyntaxContext, +) -> Option> { + fn f<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ref_count: usize, + ctxt: SyntaxContext, + ) -> Option> { + match pat.kind { + PatKind::Wild => Some(OptionPat::Wild), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), + PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { + Some(OptionPat::None) + }, + PatKind::TupleStruct(ref qpath, [pattern], _) + if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => + { + Some(OptionPat::Some { pattern, ref_count }) + }, + _ => None, + } + } + f(cx, pat, 0, ctxt) +} + +// Checks for the `None` value. +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) +} diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 37049f8357751..168c1e4d2e60d 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -221,7 +221,6 @@ fn iter_matching_struct_fields<'a>( #[expect(clippy::similar_names)] impl<'a> NormalizedPat<'a> { - #[expect(clippy::too_many_lines)] fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, @@ -235,9 +234,8 @@ impl<'a> NormalizedPat<'a> { Self::Struct(cx.qpath_res(path, pat.hir_id).opt_def_id(), fields) }, PatKind::TupleStruct(ref path, pats, wild_idx) => { - let adt = match cx.typeck_results().pat_ty(pat).ty_adt_def() { - Some(x) => x, - None => return Self::Wild, + let Some(adt) = cx.typeck_results().pat_ty(pat).ty_adt_def() else { + return Self::Wild }; let (var_id, variant) = if adt.is_enum() { match cx.qpath_res(path, pat.hir_id).opt_def_id() { diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 68682cedf1de4..1bf8d4e96ad49 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -58,6 +58,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, Some(span), + true, ); span_lint_and_sugg( @@ -90,6 +91,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, None, + true, ); (expr.span, sugg) }, @@ -107,10 +109,14 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e }, PatKind::Wild => { if ex.can_have_side_effects() { - let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0)); - let sugg = format!( - "{};\n{indent}{snippet_body}", - snippet_with_applicability(cx, ex.span, "..", &mut applicability) + let sugg = sugg_with_curlies( + cx, + (ex, expr), + (bind_names, matched_vars), + &snippet_body, + &mut applicability, + None, + false, ); span_lint_and_sugg( @@ -169,6 +175,7 @@ fn sugg_with_curlies<'a>( snippet_body: &str, applicability: &mut Applicability, assignment: Option, + needs_var_binding: bool, ) -> String { let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); @@ -200,9 +207,15 @@ fn sugg_with_curlies<'a>( s }); - format!( - "{cbrace_start}let {} = {};\n{indent}{assignment_str}{snippet_body}{cbrace_end}", - snippet_with_applicability(cx, bind_names, "..", applicability), - snippet_with_applicability(cx, matched_vars, "..", applicability) - ) + let scrutinee = if needs_var_binding { + format!( + "let {} = {}", + snippet_with_applicability(cx, bind_names, "..", applicability), + snippet_with_applicability(cx, matched_vars, "..", applicability) + ) + } else { + snippet_with_applicability(cx, matched_vars, "..", applicability).to_string() + }; + + format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index e6b183fc05f25..7d8171ead89e1 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,7 +1,9 @@ mod collapsible_match; mod infallible_destructuring_match; +mod manual_filter; mod manual_map; mod manual_unwrap_or; +mod manual_utils; mod match_as_ref; mod match_bool; mod match_like_matches; @@ -898,6 +900,34 @@ declare_clippy_lint! { "reimplementation of `map`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `match` which could be implemented using `filter` + /// + /// ### Why is this bad? + /// Using the `filter` method is clearer and more concise. + /// + /// ### Example + /// ```rust + /// match Some(0) { + /// Some(x) => if x % 2 == 0 { + /// Some(x) + /// } else { + /// None + /// }, + /// None => None, + /// }; + /// ``` + /// Use instead: + /// ```rust + /// Some(0).filter(|&x| x % 2 == 0); + /// ``` + #[clippy::version = "1.66.0"] + pub MANUAL_FILTER, + complexity, + "reimplentation of `filter`" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -939,6 +969,7 @@ impl_lint_pass!(Matches => [ SIGNIFICANT_DROP_IN_SCRUTINEE, TRY_ERR, MANUAL_MAP, + MANUAL_FILTER, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -988,6 +1019,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if !in_constant(cx, expr.hir_id) { manual_unwrap_or::check(cx, expr, ex, arms); manual_map::check_match(cx, expr, ex, arms); + manual_filter::check_match(cx, ex, arms, expr); } if self.infallible_destructuring_match_linted { @@ -1014,6 +1046,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } if !in_constant(cx, expr.hir_id) { manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr); + manual_filter::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_then, + else_expr, + ); } } redundant_pattern_match::check_if_let( diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index d496107ffd6b8..e5a15b2e1a1d2 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{expr_block, snippet}; -use clippy_utils::ty::{implements_trait, match_type, peel_mid_ty_refs}; -use clippy_utils::{ - is_lint_allowed, is_unit_expr, is_wild, paths, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, -}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs}; +use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs}; use core::cmp::max; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; @@ -156,10 +155,10 @@ fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> /// Returns `true` if the given type is an enum we know won't be expanded in the future fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool { // list of candidate `Enum`s we know will never get any more members - let candidates = [&paths::COW, &paths::OPTION, &paths::RESULT]; + let candidates = [sym::Cow, sym::Option, sym::Result]; for candidate_ty in candidates { - if match_type(cx, ty, candidate_ty) { + if is_type_diagnostic_item(cx, ty, candidate_ty) { return true; } } diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index be56b63506a4b..8adf9e3705920 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -42,9 +42,8 @@ pub(super) fn check( fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(Symbol, &'static str)> { has_iter_method(cx, self_ref_ty).map(|ty_name| { - let mutbl = match self_ref_ty.kind() { - ty::Ref(_, _, mutbl) => mutbl, - _ => unreachable!(), + let ty::Ref(_, _, mutbl) = self_ref_ty.kind() else { + unreachable!() }; let method_name = match mutbl { hir::Mutability::Not => "iter", diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index ec694cf6882e5..b80541b86479a 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -21,11 +21,7 @@ pub fn check( return; } - let mm = if let Some(mm) = is_min_or_max(cx, unwrap_arg) { - mm - } else { - return; - }; + let Some(mm) = is_min_or_max(cx, unwrap_arg) else { return }; if ty.is_signed() { use self::{ @@ -33,9 +29,7 @@ pub fn check( Sign::{Neg, Pos}, }; - let sign = if let Some(sign) = lit_sign(arith_rhs) { - sign - } else { + let Some(sign) = lit_sign(arith_rhs) else { return; }; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index cfcf9596c50d3..fb92779be2a7a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -102,9 +102,7 @@ use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{ - contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty, -}; +use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; @@ -3372,7 +3370,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { then { 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.def_id.to_def_id()).self_ty().skip_binder(); + let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()) + .self_ty() + .skip_binder(); wrong_self_convention::check( cx, item.ident.name.as_str(), @@ -3380,7 +3380,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { first_arg_ty, first_arg_span, false, - true + true, ); } } @@ -3389,7 +3389,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if item.ident.name == sym::new; if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id()); - let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder(); + let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()) + .self_ty() + .skip_binder(); if !ret_ty.contains(self_ty); then { @@ -3846,14 +3848,13 @@ impl SelfKind { return m == mutability && t == parent_ty; } - let trait_path = match mutability { - hir::Mutability::Not => &paths::ASREF_TRAIT, - hir::Mutability::Mut => &paths::ASMUT_TRAIT, + let trait_sym = match mutability { + hir::Mutability::Not => sym::AsRef, + hir::Mutability::Mut => sym::AsMut, }; - let trait_def_id = match get_trait_def_id(cx, trait_path) { - Some(did) => did, - None => return false, + let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { + return false }; implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) } diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 6fb92d1c663cf..742483e6b2e55 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -32,8 +32,7 @@ pub(super) fn check<'tcx>( return; } - let deref_aliases: [&[&str]; 9] = [ - &paths::DEREF_TRAIT_METHOD, + let deref_aliases: [&[&str]; 8] = [ &paths::DEREF_MUT_TRAIT_METHOD, &paths::CSTRING_AS_C_STR, &paths::OS_STRING_AS_OS_STR, @@ -45,12 +44,14 @@ pub(super) fn check<'tcx>( ]; let is_deref = match map_arg.kind { - hir::ExprKind::Path(ref expr_qpath) => cx - .qpath_res(expr_qpath, map_arg.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::Path(ref expr_qpath) => { + cx.qpath_res(expr_qpath, map_arg.hir_id) + .opt_def_id() + .map_or(false, |fun_def_id| { + cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id) + || deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) + }) + }, hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let closure_body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(closure_body.value); @@ -68,7 +69,8 @@ pub(super) fn check<'tcx>( if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; then { let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); - deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) + cx.tcx.is_diagnostic_item(sym::deref_method, method_did) + || deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) } else { false } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 6a35024d03612..991d3dd538bf8 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; -use clippy_utils::ty::{implements_trait, match_type}; -use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{contains_return, is_trait_item, last_path_segment}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Symbol}; use std::borrow::Cow; use super::OR_FUN_CALL; @@ -88,11 +88,11 @@ pub(super) fn check<'tcx>( fun_span: Option, ) { // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ - (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), - (&paths::RESULT, true, &["or", "unwrap_or"], "else"), + const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [ + (sym::BTreeEntry, false, &["or_insert"], "with"), + (sym::HashMapEntry, false, &["or_insert"], "with"), + (sym::Option, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), + (sym::Result, true, &["or", "unwrap_or"], "else"), ]; if_chain! { @@ -104,7 +104,7 @@ pub(super) fn check<'tcx>( let self_ty = cx.typeck_results().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| is_type_diagnostic_item(cx, self_ty, i.0)); if poss.contains(&name); @@ -121,10 +121,9 @@ pub(super) fn check<'tcx>( macro_expanded_snipped = snippet(cx, snippet_span, ".."); match macro_expanded_snipped.strip_prefix("$crate::vec::") { Some(stripped) => Cow::from(stripped), - None => macro_expanded_snipped + None => macro_expanded_snipped, } - } - else { + } else { not_macro_argument_snippet } }; diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index ae3594bd36c3a..1acac59144cee 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -289,9 +289,7 @@ fn parse_iter_usage<'tcx>( ) -> Option { let (kind, span) = match iter.next() { Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { - let (name, args) = if let ExprKind::MethodCall(name, _, [args @ ..], _) = e.kind { - (name, args) - } else { + let ExprKind::MethodCall(name, _, [args @ ..], _) = e.kind else { return None; }; let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?; diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 5cf88bfc8880d..3566fe9a0bbc9 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -363,7 +363,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< && let output_ty = return_ty(cx, item.hir_id()) && let local_def_id = cx.tcx.hir().local_def_id(item.hir_id()) && Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.hir_id()); + let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.hir_id()); fn_ctxt.can_coerce(ty, output_ty) }) { if has_lifetime(output_ty) && has_lifetime(ty) { diff --git a/clippy_lints/src/misc_early/literal_suffix.rs b/clippy_lints/src/misc_early/literal_suffix.rs index 62c6ca32d31a9..27e7f8505eb5b 100644 --- a/clippy_lints/src/misc_early/literal_suffix.rs +++ b/clippy_lints/src/misc_early/literal_suffix.rs @@ -6,9 +6,7 @@ use rustc_lint::EarlyContext; use super::{SEPARATED_LITERAL_SUFFIX, UNSEPARATED_LITERAL_SUFFIX}; pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) { - let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { - val - } else { + let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; // Do not lint when literal is unsuffixed. diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index 80e2421310078..263ee1e945a25 100644 --- a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -5,9 +5,7 @@ use rustc_lint::EarlyContext; use super::MIXED_CASE_HEX_LITERALS; pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) { - let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { - val - } else { + let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; if maybe_last_sep_idx <= 2 { diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 4963bba82f2da..9ead43ea4a477 100644 --- a/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -6,6 +6,7 @@ use rustc_lint::EarlyContext; use super::ZERO_PREFIXED_LITERAL; pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { + let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0'); span_lint_and_then( cx, ZERO_PREFIXED_LITERAL, @@ -15,15 +16,18 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { diag.span_suggestion( lit.span, "if you mean to use a decimal constant, remove the `0` to avoid confusion", - lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(), - Applicability::MaybeIncorrect, - ); - diag.span_suggestion( - lit.span, - "if you mean to use an octal constant, use `0o`", - format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')), + trimmed_lit_snip.to_string(), Applicability::MaybeIncorrect, ); + // do not advise to use octal form if the literal cannot be expressed in base 8. + if !lit_snip.contains(|c| c == '8' || c == '9') { + diag.span_suggestion( + lit.span, + "if you mean to use an octal constant, use `0o`", + format!("0o{trimmed_lit_snip}"), + Applicability::MaybeIncorrect, + ); + } }, ); } diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index 6dd76a6531e49..9de4b56b77b56 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -70,9 +70,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { // find the type that the Impl is for // only lint on struct/enum/union for now - let defid = match path.res { - Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid, - _ => return, + let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) = path.res else { + return }; // get the names of the generic parameters in the type diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs new file mode 100644 index 0000000000000..68af8a672f6ae --- /dev/null +++ b/clippy_lints/src/missing_trait_methods.rs @@ -0,0 +1,98 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_lint_allowed; +use clippy_utils::macros::span_is_local; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Impl, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::AssocItem; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks if a provided method is used implicitly by a trait + /// implementation. A usage example would be a wrapper where every method + /// should perform some operation before delegating to the inner type's + /// implemenation. + /// + /// This lint should typically be enabled on a specific trait `impl` item + /// rather than globally. + /// + /// ### Why is this bad? + /// Indicates that a method is missing. + /// + /// ### Example + /// ```rust + /// trait Trait { + /// fn required(); + /// + /// fn provided() {} + /// } + /// + /// # struct Type; + /// #[warn(clippy::missing_trait_methods)] + /// impl Trait for Type { + /// fn required() { /* ... */ } + /// } + /// ``` + /// Use instead: + /// ```rust + /// trait Trait { + /// fn required(); + /// + /// fn provided() {} + /// } + /// + /// # struct Type; + /// #[warn(clippy::missing_trait_methods)] + /// impl Trait for Type { + /// fn required() { /* ... */ } + /// + /// fn provided() { /* ... */ } + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub MISSING_TRAIT_METHODS, + restriction, + "trait implementation uses default provided method" +} +declare_lint_pass!(MissingTraitMethods => [MISSING_TRAIT_METHODS]); + +impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if !is_lint_allowed(cx, MISSING_TRAIT_METHODS, item.hir_id()) + && span_is_local(item.span) + && let ItemKind::Impl(Impl { + items, + of_trait: Some(trait_ref), + .. + }) = item.kind + && let Some(trait_id) = trait_ref.trait_def_id() + { + let mut provided: DefIdMap<&AssocItem> = cx + .tcx + .provided_trait_methods(trait_id) + .map(|assoc| (assoc.def_id, assoc)) + .collect(); + + for impl_item in *items { + if let Some(def_id) = impl_item.trait_item_def_id { + provided.remove(&def_id); + } + } + + for assoc in provided.values() { + let source_map = cx.tcx.sess.source_map(); + let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id)); + + span_lint_and_help( + cx, + MISSING_TRAIT_METHODS, + source_map.guess_head_span(item.span), + &format!("missing trait method provided by default: `{}`", assoc.name), + Some(definition_span), + "implement the method", + ); + } + } + } +} diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index a2419c277e9c2..6752976348f60 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -190,10 +190,7 @@ fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) { if parent_id == cur_id { break; } - let parent_node = match map.find(parent_id) { - Some(parent) => parent, - None => break, - }; + let Some(parent_node) = map.find(parent_id) else { break }; let stop_early = match parent_node { Node::Expr(expr) => check_expr(vis, expr), diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 3233d87c07319..c3b633fd6a038 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -49,9 +49,8 @@ declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - let expr = match stmt.kind { - StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, - _ => return, + let (StmtKind::Expr(expr) | StmtKind::Semi(expr)) = stmt.kind else { + return }; if_chain! { 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 a7e0e35787cff..5c2b96f5b2ce6 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::implements_trait; -use clippy_utils::{self, get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::{BinOpKind, 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}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -47,18 +47,16 @@ declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]); impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if !in_external_macro(cx.sess(), expr.span); 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 { - let ty = cx.typeck_results().expr_ty(left); let implements_ord = { - if let Some(id) = get_trait_def_id(cx, &paths::ORD) { + if let Some(id) = cx.tcx.get_diagnostic_item(sym::Ord) { implements_trait(cx, ty, id, &[]) } else { return; @@ -81,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { "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" + clear that the two values could be incomparable", ); } } diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 2c839d029c6f7..a6742824bc56a 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -357,9 +357,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } // Make sure it is a const item. - let item_def_id = match cx.qpath_res(qpath, expr.hir_id) { - Res::Def(DefKind::Const | DefKind::AssocConst, did) => did, - _ => return, + let Res::Def(DefKind::Const | DefKind::AssocConst, item_def_id) = cx.qpath_res(qpath, expr.hir_id) else { + return }; // Climb up to resolve any field access and explicit referencing. diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 1a765b14892f6..2ecb04874842f 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -55,9 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { if let ExprKind::Lit(_) = param.kind; then { - let snip = match snippet_opt(cx, param.span) { - Some(s) => s, - _ => return, + let Some(snip) = snippet_opt(cx, param.span) else { + return }; if !snip.starts_with("0o") { @@ -72,16 +71,10 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { 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; - + if let Some(snip) = snippet_opt(cx, param.span); + if !snip.starts_with("0o"); then { - let snip = match snippet_opt(cx, param.span) { - Some(s) => s, - _ => return, - }; - - if !snip.starts_with("0o") { - show_error(cx, param); - } + show_error(cx, param); } } }, diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 0ca0befc13515..6c909e5ed73ea 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -266,7 +266,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { .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}`")))?, + .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, }) } } diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index c9c777f1bd8d8..24aeb82a37f31 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{match_any_def_paths, path_def_id, paths}; +use clippy_utils::{match_def_path, path_def_id, paths}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; @@ -49,13 +49,15 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path) - .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) - .map_or(false, |idx| match idx { - 0 => true, - 1 => !is_copy(cx, typeck.expr_ty(expr)), - _ => false, - }) => + if path_def_id(cx, path).map_or(false, |id| { + if match_def_path(cx, id, &paths::FROM_STR_METHOD) { + true + } else if cx.tcx.lang_items().from_fn() == Some(id) { + !is_copy(cx, typeck.expr_ty(expr)) + } else { + false + } + }) => { (arg, arg.span) }, diff --git a/clippy_lints/src/partial_pub_fields.rs b/clippy_lints/src/partial_pub_fields.rs new file mode 100644 index 0000000000000..f60d9d65b1207 --- /dev/null +++ b/clippy_lints/src/partial_pub_fields.rs @@ -0,0 +1,81 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{Item, ItemKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks whether partial fields of a struct are public. + /// + /// Either make all fields of a type public, or make none of them public + /// + /// ### Why is this bad? + /// Most types should either be: + /// * Abstract data types: complex objects with opaque implementation which guard + /// interior invariants and expose intentionally limited API to the outside world. + /// * Data: relatively simple objects which group a bunch of related attributes together. + /// + /// ### Example + /// ```rust + /// pub struct Color { + /// pub r: u8, + /// pub g: u8, + /// b: u8, + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub struct Color { + /// pub r: u8, + /// pub g: u8, + /// pub b: u8, + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub PARTIAL_PUB_FIELDS, + restriction, + "partial fields of a struct are public" +} +declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]); + +impl EarlyLintPass for PartialPubFields { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + let ItemKind::Struct(ref st, _) = item.kind else { + return; + }; + + let mut fields = st.fields().iter(); + let Some(first_field) = fields.next() else { + // Empty struct. + return; + }; + let all_pub = first_field.vis.kind.is_pub(); + let all_priv = !all_pub; + + let msg = "mixed usage of pub and non-pub fields"; + + for field in fields { + if all_priv && field.vis.kind.is_pub() { + span_lint_and_help( + cx, + PARTIAL_PUB_FIELDS, + field.vis.span, + msg, + None, + "consider using private field here", + ); + return; + } else if all_pub && !field.vis.kind.is_pub() { + span_lint_and_help( + cx, + PARTIAL_PUB_FIELDS, + field.vis.span, + msg, + None, + "consider using public field here", + ); + return; + } + } + } +} diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index d296a150b46d0..40db315bf2726 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -15,13 +15,17 @@ use rustc_hir::{ ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, Unsafety, }; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Binder, ExistentialPredicate, List, PredicateKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; use rustc_span::symbol::Symbol; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use std::fmt; use std::iter; @@ -384,6 +388,17 @@ enum DerefTy<'tcx> { Slice(Option, Ty<'tcx>), } impl<'tcx> DerefTy<'tcx> { + fn ty(&self, cx: &LateContext<'tcx>) -> Ty<'tcx> { + match *self { + Self::Str => cx.tcx.types.str_, + Self::Path => cx.tcx.mk_adt( + cx.tcx.adt_def(cx.tcx.get_diagnostic_item(sym::Path).unwrap()), + List::empty(), + ), + Self::Slice(_, ty) => cx.tcx.mk_slice(ty), + } + } + fn argless_str(&self) -> &'static str { match *self { Self::Str => "str", @@ -552,9 +567,8 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: } // Check if this is local we care about - let args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) { - Some(&i) => i, - None => return walk_expr(self, e), + let Some(&args_idx) = path_to_local(e).and_then(|id| self.bindings.get(&id)) else { + return walk_expr(self, e); }; let args = &self.args[args_idx]; let result = &mut self.results[args_idx]; @@ -582,6 +596,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0); if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| { match *ty.skip_binder().peel_refs().kind() { + ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds), ty::Param(_) => true, ty::Adt(def, _) => def.did() == args.ty_did, _ => false, @@ -609,14 +624,15 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: } } - let id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) { - x - } else { + let Some(id) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) else { set_skip_flag(); return; }; match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() { + ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => { + set_skip_flag(); + }, ty::Param(_) => { set_skip_flag(); }, @@ -668,6 +684,30 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: v.results } +fn matches_preds<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + preds: &'tcx [Binder<'tcx, ExistentialPredicate<'tcx>>], +) -> bool { + let infcx = cx.tcx.infer_ctxt().build(); + preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) { + ExistentialPredicate::Trait(p) => infcx + .type_implements_trait(p.def_id, ty, p.substs, cx.param_env) + .must_apply_modulo_regions(), + ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new( + ObligationCause::dummy(), + cx.param_env, + cx.tcx.mk_predicate(Binder::bind_with_vars( + PredicateKind::Projection(p.with_self_ty(cx.tcx, ty)), + List::empty(), + )), + )), + ExistentialPredicate::AutoTrait(p) => infcx + .type_implements_trait(p, ty, List::empty(), cx.param_env) + .must_apply_modulo_regions(), + }) +} + fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { if let TyKind::Rptr(lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index b0a5d1a675828..72dda67c72b25 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -49,15 +49,13 @@ declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]); impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call - let (receiver_expr, arg_expr, method) = match expr_as_ptr_offset_call(cx, expr) { - Some(call_arg) => call_arg, - None => return, + let Some((receiver_expr, arg_expr, method)) = expr_as_ptr_offset_call(cx, expr) else { + return }; // Check if the argument to the method call is a cast from usize - let cast_lhs_expr = match expr_as_cast_from_usize(cx, arg_expr) { - Some(cast_lhs_expr) => cast_lhs_expr, - None => return, + let Some(cast_lhs_expr) = expr_as_cast_from_usize(cx, arg_expr) else { + return }; let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 9fd86331ec755..aedbe08e3e46e 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,25 +1,18 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use if_chain::if_chain; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{def_id, Body, FnDecl, HirId}; -use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::{ - self, traversal, - visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _}, - Mutability, -}; -use rustc_middle::ty::{self, visit::TypeVisitor, Ty}; -use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis, ResultsCursor}; +use rustc_middle::mir; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::sym; -use std::ops::ControlFlow; macro_rules! unwrap_or_continue { ($x:expr) => { @@ -89,21 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let mir = cx.tcx.optimized_mir(def_id.to_def_id()); - let possible_origin = { - let mut vis = PossibleOriginVisitor::new(mir); - vis.visit_body(mir); - vis.into_map(cx) - }; - let maybe_storage_live_result = MaybeStorageLive - .into_engine(cx.tcx, mir) - .pass_name("redundant_clone") - .iterate_to_fixpoint() - .into_results_cursor(mir); - let mut possible_borrower = { - let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); - vis.visit_body(mir); - vis.into_map(cx, maybe_storage_live_result) - }; + let mut possible_borrower = PossibleBorrowerMap::new(cx, mir); for (bb, bbdata) in mir.basic_blocks.iter_enumerated() { let terminator = bbdata.terminator(); @@ -374,403 +353,40 @@ struct CloneUsage { /// Whether the clone value is mutated. clone_consumed_or_mutated: bool, } -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 }); - } - - self.visit_terminator( - data.terminator(), - mir::Location { - block, - statement_index: statements.len(), - }, - ); - } - - 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`. -#[derive(Copy, Clone)] -struct MaybeStorageLive; - -impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { - type Domain = BitSet; - const NAME: &'static str = "maybe_storage_live"; - - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - // bottom = dead - BitSet::new_empty(body.local_decls.len()) - } - - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { - for arg in body.args_iter() { - state.insert(arg); - } - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { - type Idx = mir::Local; - - fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { - match stmt.kind { - mir::StatementKind::StorageLive(l) => trans.gen(l), - mir::StatementKind::StorageDead(l) => trans.kill(l), - _ => (), - } - } - - fn terminator_effect( - &self, - _trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - _loc: mir::Location, - ) { - } - - fn call_return_effect( - &self, - _trans: &mut impl GenKill, - _block: mir::BasicBlock, - _return_places: CallReturnPlaces<'_, 'tcx>, - ) { - // Nothing to do when a call returns successfully - } -} - -/// Collects the possible borrowers of each local. -/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` -/// possible borrowers of `a`. -struct PossibleBorrowerVisitor<'a, 'tcx> { - possible_borrower: TransitiveRelation, - body: &'a mir::Body<'tcx>, - cx: &'a LateContext<'tcx>, - possible_origin: FxHashMap>, -} - -impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> { - fn new( - cx: &'a LateContext<'tcx>, - body: &'a mir::Body<'tcx>, - possible_origin: FxHashMap>, - ) -> Self { - Self { - possible_borrower: TransitiveRelation::default(), - cx, - body, - possible_origin, - } - } - - fn into_map( - self, - cx: &LateContext<'tcx>, - maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>, - ) -> PossibleBorrowerMap<'a, 'tcx> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } - - let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); - if !borrowers.is_empty() { - map.insert(row, borrowers); - } - } - - let bs = BitSet::new_empty(self.body.local_decls.len()); - PossibleBorrowerMap { - map, - maybe_live, - bitset: (bs.clone(), bs), - } - } -} - -impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - mir::Rvalue::Ref(_, _, borrowed) => { - self.possible_borrower.add(borrowed.local, lhs); - }, - other => { - if ContainsRegion - .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) - .is_continue() - { - return; - } - rvalue_locals(other, |rhs| { - if lhs != rhs { - self.possible_borrower.add(rhs, lhs); - } - }); - }, - } - } - - fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { - if let mir::TerminatorKind::Call { - args, - destination: mir::Place { local: dest, .. }, - .. - } = &terminator.kind - { - // TODO add doc - // 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`. - - let mut immutable_borrowers = vec![]; - let mut mutable_borrowers = vec![]; - - for op in args { - match op { - mir::Operand::Copy(p) | mir::Operand::Move(p) => { - if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() { - mutable_borrowers.push(p.local); - } else { - immutable_borrowers.push(p.local); - } - }, - mir::Operand::Constant(..) => (), - } - } - - let mut mutable_variables: Vec = mutable_borrowers - .iter() - .filter_map(|r| self.possible_origin.get(r)) - .flat_map(HybridBitSet::iter) - .collect(); - - if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { - mutable_variables.push(*dest); - } - - for y in mutable_variables { - for x in &immutable_borrowers { - self.possible_borrower.add(*x, y); - } - for x in &mutable_borrowers { - self.possible_borrower.add(*x, y); - } - } - } - } -} - -/// Collect possible borrowed for every `&mut` local. -/// For example, `_1 = &mut _2` generate _1: {_2,...} -/// Known Problems: not sure all borrowed are tracked -struct PossibleOriginVisitor<'a, 'tcx> { - possible_origin: TransitiveRelation, - body: &'a mir::Body<'tcx>, -} - -impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { - fn new(body: &'a mir::Body<'tcx>) -> Self { - Self { - possible_origin: TransitiveRelation::default(), - body, - } - } - - fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } - - let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); - if !borrowers.is_empty() { - map.insert(row, borrowers); - } - } - map - } -} - -impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - // Only consider `&mut`, which can modify origin place - mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) | - // _2: &mut _; - // _3 = move _2 - mir::Rvalue::Use(mir::Operand::Move(borrowed)) | - // _3 = move _2 as &mut _; - mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _) - => { - self.possible_origin.add(lhs, borrowed.local); - }, - _ => {}, - } - } -} - -struct ContainsRegion; - -impl TypeVisitor<'_> for ContainsRegion { - type BreakTy = (); - - fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow { - ControlFlow::BREAK - } -} - -fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { - use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; - - 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 { - Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), - Aggregate(_, ops) => ops.iter().for_each(visit_op), - BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { - visit_op(lhs); - visit_op(rhs); +fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage { + if let Some(( + LocalUsage { + local_use_locs: cloned_use_locs, + local_consume_or_mutate_locs: cloned_consume_or_mutate_locs, }, - _ => (), - } -} - -/// Result of `PossibleBorrowerVisitor`. -struct PossibleBorrowerMap<'a, 'tcx> { - /// Mapping `Local -> its possible borrowers` - map: FxHashMap>, - maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>, - // Caches to avoid allocation of `BitSet` on every query - bitset: (BitSet, BitSet), -} - -impl PossibleBorrowerMap<'_, '_> { - /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. - fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { - self.maybe_live.seek_after_primary_effect(at); - - self.bitset.0.clear(); - let maybe_live = &mut self.maybe_live; - if let Some(bitset) = self.map.get(&borrowed) { - for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { - self.bitset.0.insert(b); - } - } else { - return false; - } - - self.bitset.1.clear(); - for b in borrowers { - self.bitset.1.insert(*b); + LocalUsage { + local_use_locs: _, + local_consume_or_mutate_locs: clone_consume_or_mutate_locs, + }, + )) = visit_local_usage( + &[cloned, clone], + mir, + mir::Location { + block: bb, + statement_index: mir.basic_blocks[bb].statements.len(), + }, + ) + .map(|mut vec| (vec.remove(0), vec.remove(0))) + { + CloneUsage { + cloned_used: !cloned_use_locs.is_empty(), + cloned_consume_or_mutate_loc: cloned_consume_or_mutate_locs.first().copied(), + // Consider non-temporary clones consumed. + // TODO: Actually check for mutation of non-temporaries. + clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp + || !clone_consume_or_mutate_locs.is_empty(), } - - self.bitset.0 == self.bitset.1 - } - - fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { - self.maybe_live.seek_after_primary_effect(at); - self.maybe_live.contains(local) - } -} - -#[derive(Default)] -struct TransitiveRelation { - relations: FxHashMap>, -} -impl TransitiveRelation { - fn add(&mut self, a: mir::Local, b: mir::Local) { - self.relations.entry(a).or_default().push(b); - } - - fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet { - let mut seen = HybridBitSet::new_empty(domain_size); - let mut stack = vec![a]; - while let Some(u) = stack.pop() { - if let Some(edges) = self.relations.get(&u) { - for &v in edges { - if seen.insert(v) { - stack.push(v); - } - } - } + } else { + CloneUsage { + cloned_used: true, + cloned_consume_or_mutate_loc: None, + clone_consumed_or_mutated: true, } - seen } } diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 42514f861be1c..f21b3ea6c3b05 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -52,7 +52,8 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { GenericArg::Type(inner_ty) => Some(inner_ty), _ => None, }); - if let TyKind::Rptr(_, _) = inner_ty.kind; + if let TyKind::Rptr(_, ref inner_mut_ty) = inner_ty.kind; + if inner_mut_ty.mutbl == Mutability::Not; then { span_lint_and_sugg( diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 5dcdab5b8ab90..87f966ced0df1 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -106,10 +106,7 @@ impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]); impl<'tcx> LateLintPass<'tcx> for Shadow { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - let (id, ident) = match pat.kind { - PatKind::Binding(_, hir_id, ident, _) => (hir_id, ident), - _ => return, - }; + let PatKind::Binding(_, id, ident, _) = pat.kind else { return }; if pat.span.desugaring_kind().is_some() { return; diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 70d166c4854c4..2f190e594a831 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -6,11 +6,7 @@ use rustc_span::DUMMY_SP; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment -pub(super) fn is_layout_incompatible<'tcx>( - cx: &LateContext<'tcx>, - from: Ty<'tcx>, - to: Ty<'tcx>, -) -> bool { +pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from) && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to) && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from)) @@ -33,9 +29,7 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, ) -> bool { - use CastKind::{ - AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast, - }; + use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; matches!( check_cast(cx, e, from_ty, to_ty), Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) @@ -46,20 +40,18 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( /// 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. -fn check_cast<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, -) -> Option { +fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option { let hir_id = e.hir_id; let local_def_id = hir_id.owner.def_id; Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id); + let fn_ctxt = FnCtxt::new(inherited, cx.param_env, hir_id); // If we already have errors, we can't be sure we can pointer cast. - assert!(!fn_ctxt.errors_reported_since_creation(), "Newly created FnCtxt contained errors"); + assert!( + !fn_ctxt.errors_reported_since_creation(), + "Newly created FnCtxt contained errors" + ); if let Ok(check) = cast::CastCheck::new( &fn_ctxt, e, from_ty, to_ty, diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index 6b9de64e24c93..fa567b9b2d243 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -9,6 +9,7 @@ use rustc_span::symbol::sym; use super::RC_BUFFER; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + let app = Applicability::Unspecified; if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { if let Some(alternate) = match_buffer_type(cx, qpath) { span_lint_and_sugg( @@ -18,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ "usage of `Rc` when T is a buffer type", "try", format!("Rc<{alternate}>"), - Applicability::MachineApplicable, + app, ); } else { let Some(ty) = qpath_generic_tys(qpath).next() else { return false }; @@ -26,15 +27,12 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ if !cx.tcx.is_diagnostic_item(sym::Vec, id) { return false; } - let qpath = match &ty.kind { - TyKind::Path(qpath) => qpath, - _ => return false, - }; + let TyKind::Path(qpath) = &ty.kind else { return false }; let inner_span = match qpath_generic_tys(qpath).next() { Some(ty) => ty.span, None => return false, }; - let mut applicability = Applicability::MachineApplicable; + let mut applicability = app; span_lint_and_sugg( cx, RC_BUFFER, @@ -45,7 +43,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ "Rc<[{}]>", snippet_with_applicability(cx, inner_span, "..", &mut applicability) ), - Applicability::MachineApplicable, + app, ); return true; } @@ -58,22 +56,19 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ "usage of `Arc` when T is a buffer type", "try", format!("Arc<{alternate}>"), - Applicability::MachineApplicable, + app, ); } else if let Some(ty) = qpath_generic_tys(qpath).next() { let Some(id) = path_def_id(cx, ty) else { return false }; if !cx.tcx.is_diagnostic_item(sym::Vec, id) { return false; } - let qpath = match &ty.kind { - TyKind::Path(qpath) => qpath, - _ => return false, - }; + let TyKind::Path(qpath) = &ty.kind else { return false }; let inner_span = match qpath_generic_tys(qpath).next() { Some(ty) => ty.span, None => return false, }; - let mut applicability = Applicability::MachineApplicable; + let mut applicability = app; span_lint_and_sugg( cx, RC_BUFFER, @@ -84,7 +79,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ "Arc<[{}]>", snippet_with_applicability(cx, inner_span, "..", &mut applicability) ), - Applicability::MachineApplicable, + app, ); return true; } diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index ecb6720053908..7883353e3fef6 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -10,6 +10,7 @@ use rustc_span::symbol::sym; use super::{utils, REDUNDANT_ALLOCATION}; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + let mut applicability = Applicability::MaybeIncorrect; let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() { "Box" } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { @@ -21,7 +22,6 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }; if let Some(span) = utils::match_borrows_parameter(cx, qpath) { - let mut applicability = Applicability::MaybeIncorrect; let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_then( cx, @@ -47,9 +47,8 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ _ => return false, }; - let inner_qpath = match &ty.kind { - TyKind::Path(inner_qpath) => inner_qpath, - _ => return false, + let TyKind::Path(inner_qpath) = &ty.kind else { + return false }; let inner_span = match qpath_generic_tys(inner_qpath).next() { Some(ty) => { @@ -64,7 +63,6 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ None => return false, }; if inner_sym == outer_sym { - let mut applicability = Applicability::MaybeIncorrect; let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability); span_lint_and_then( cx, diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index 57aff5367dd15..1307288623f95 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::{get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; @@ -7,7 +6,7 @@ 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}; +use rustc_span::{sym, BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -80,7 +79,7 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve 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 ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::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 @@ -99,11 +98,15 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve 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| Some(ord.self_ty()) == return_ty_pred.term.ty()) { + if ord_preds + .iter() + .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) + { args_to_check.push((i, "Ord".to_string())); - } else if partial_ord_preds.iter().any(|pord| { - pord.self_ty() == return_ty_pred.term.ty().unwrap() - }) { + } else if partial_ord_preds + .iter() + .any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap()) + { args_to_check.push((i, "PartialOrd".to_string())); } } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index fb73c386640b4..b305dae76084c 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -163,9 +163,8 @@ fn unnest_or_patterns(pat: &mut P) -> bool { 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, + let Or(alternatives) = &mut p.kind else { + return }; // Collapse or-patterns directly nested in or-patterns. diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 8bcdff66331d1..92053cec59fc8 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -47,9 +47,8 @@ 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(expr) | hir::StmtKind::Expr(expr) => expr, - _ => return, + let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = s.kind else { + return }; match expr.kind { diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index a82643a59f97b..1f69db1cbca40 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -55,9 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { match e.kind { ExprKind::Match(_, arms, MatchSource::TryDesugar) => { - let e = match arms[0].body.kind { - ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e)) => e, - _ => return, + let (ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e))) = arms[0].body.kind else { + return }; if let ExprKind::Call(_, [arg, ..]) = e.kind { self.try_desugar_arm.push(arg.hir_id); diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index e069de8cb5c7e..0c052d86eda40 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -12,6 +12,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::{Ident, Symbol}; +use std::cell::Cell; use std::fmt::{Display, Formatter, Write as _}; declare_clippy_lint! { @@ -37,15 +38,13 @@ declare_clippy_lint! { /// /// ```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 - /// } + /// if ExprKind::If(ref cond, ref then, None) = item.kind + /// && let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind + /// && let ExprKind::Path(ref path) = left.kind + /// && let ExprKind::Lit(ref lit) = right.kind + /// && let LitKind::Int(42, _) = lit.node + /// { + /// // report your lint here /// } /// ``` pub LINT_AUTHOR, @@ -91,15 +90,16 @@ macro_rules! field { }; } -fn prelude() { - println!("if_chain! {{"); -} - -fn done() { - println!(" then {{"); - println!(" // report your lint here"); - println!(" }}"); - println!("}}"); +/// Print a condition of a let chain, `chain!(self, "let Some(x) = y")` will print +/// `if let Some(x) = y` on the first call and ` && let Some(x) = y` thereafter +macro_rules! chain { + ($self:ident, $($t:tt)*) => { + if $self.first.take() { + println!("if {}", format_args!($($t)*)); + } else { + println!(" && {}", format_args!($($t)*)); + } + } } impl<'tcx> LateLintPass<'tcx> for Author { @@ -149,9 +149,10 @@ fn check_item(cx: &LateContext<'_>, hir_id: HirId) { fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) { if has_attr(cx, hir_id) { - prelude(); f(&PrintVisitor::new(cx)); - done(); + println!("{{"); + println!(" // report your lint here"); + println!("}}"); } } @@ -195,7 +196,9 @@ struct PrintVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, /// Fields are the current index that needs to be appended to pattern /// binding names - ids: std::cell::Cell>, + ids: Cell>, + /// Currently at the first condition in the if chain + first: Cell, } #[allow(clippy::unused_self)] @@ -203,7 +206,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, - ids: std::cell::Cell::default(), + ids: Cell::default(), + first: Cell::new(true), } } @@ -226,10 +230,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn option(&self, option: &Binding>, name: &'static str, f: impl Fn(&Binding)) { match option.value { - None => out!("if {option}.is_none();"), + None => chain!(self, "{option}.is_none()"), Some(value) => { let value = &self.bind(name, value); - out!("if let Some({value}) = {option};"); + chain!(self, "let Some({value}) = {option}"); f(value); }, } @@ -237,9 +241,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn slice(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) { if slice.value.is_empty() { - out!("if {slice}.is_empty();"); + chain!(self, "{slice}.is_empty()"); } else { - out!("if {slice}.len() == {};", slice.value.len()); + chain!(self, "{slice}.len() == {}", slice.value.len()); for (i, value) in slice.value.iter().enumerate() { let name = format!("{slice}[{i}]"); f(&Binding { name, value }); @@ -254,23 +258,23 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn ident(&self, ident: &Binding) { - out!("if {ident}.as_str() == {:?};", ident.value.as_str()); + chain!(self, "{ident}.as_str() == {:?}", ident.value.as_str()); } fn symbol(&self, symbol: &Binding) { - out!("if {symbol}.as_str() == {:?};", symbol.value.as_str()); + chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } fn qpath(&self, qpath: &Binding<&QPath<'_>>) { if let QPath::LangItem(lang_item, ..) = *qpath.value { - out!("if matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _));"); + chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); } else { - out!("if match_qpath({qpath}, &[{}]);", path_to_string(qpath.value)); + chain!(self, "match_qpath({qpath}, &[{}])", path_to_string(qpath.value)); } } fn lit(&self, lit: &Binding<&Lit>) { - let kind = |kind| out!("if let LitKind::{kind} = {lit}.node;"); + let kind = |kind| chain!(self, "let LitKind::{kind} = {lit}.node"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -298,7 +302,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { LitKind::ByteStr(ref vec) => { bind!(self, vec); kind!("ByteStr(ref {vec})"); - out!("if let [{:?}] = **{vec};", vec.value); + chain!(self, "let [{:?}] = **{vec}", vec.value); }, LitKind::Str(s, _) => { bind!(self, s); @@ -311,15 +315,15 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn arm(&self, arm: &Binding<&hir::Arm<'_>>) { self.pat(field!(arm.pat)); match arm.value.guard { - None => out!("if {arm}.guard.is_none();"), + None => chain!(self, "{arm}.guard.is_none()"), Some(hir::Guard::If(expr)) => { bind!(self, expr); - out!("if let Some(Guard::If({expr})) = {arm}.guard;"); + chain!(self, "let Some(Guard::If({expr})) = {arm}.guard"); self.expr(expr); }, Some(hir::Guard::IfLet(let_expr)) => { bind!(self, let_expr); - out!("if let Some(Guard::IfLet({let_expr}) = {arm}.guard;"); + chain!(self, "let Some(Guard::IfLet({let_expr}) = {arm}.guard"); self.pat(field!(let_expr.pat)); self.expr(field!(let_expr.init)); }, @@ -331,9 +335,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn expr(&self, expr: &Binding<&hir::Expr<'_>>) { if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) { bind!(self, condition, body); - out!( - "if let Some(higher::While {{ condition: {condition}, body: {body} }}) \ - = higher::While::hir({expr});" + chain!( + self, + "let Some(higher::While {{ condition: {condition}, body: {body} }}) \ + = higher::While::hir({expr})" ); self.expr(condition); self.expr(body); @@ -347,9 +352,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { }) = higher::WhileLet::hir(expr.value) { bind!(self, let_pat, let_expr, if_then); - out!( - "if let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ - = higher::WhileLet::hir({expr});" + chain!( + self, + "let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ + = higher::WhileLet::hir({expr})" ); self.pat(let_pat); self.expr(let_expr); @@ -359,9 +365,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) { bind!(self, pat, arg, body); - out!( - "if let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ - = higher::ForLoop::hir({expr});" + chain!( + self, + "let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ + = higher::ForLoop::hir({expr})" ); self.pat(pat); self.expr(arg); @@ -369,7 +376,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { return; } - let kind = |kind| out!("if let ExprKind::{kind} = {expr}.kind;"); + let kind = |kind| chain!(self, "let ExprKind::{kind} = {expr}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -383,7 +390,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { // if it's a path if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { bind!(self, qpath); - out!("if let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind;"); + chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); self.qpath(qpath); } self.expr(field!(let_expr.init)); @@ -419,7 +426,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::Binary(op, left, right) => { bind!(self, op, left, right); kind!("Binary({op}, {left}, {right})"); - out!("if BinOpKind::{:?} == {op}.node;", op.value.node); + chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); self.expr(left); self.expr(right); }, @@ -438,7 +445,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Cast({expr}, {cast_ty})"); if let TyKind::Path(ref qpath) = cast_ty.value.kind { bind!(self, qpath); - out!("if let TyKind::Path(ref {qpath}) = {cast_ty}.kind;"); + chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind"); self.qpath(qpath); } self.expr(expr); @@ -485,7 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, fn_decl, body_id); kind!("Closure(CaptureBy::{capture_clause:?}, {fn_decl}, {body_id}, _, {movability})"); - out!("if let {ret_ty} = {fn_decl}.output;"); + chain!(self, "let {ret_ty} = {fn_decl}.output"); self.body(body_id); }, ExprKind::Yield(sub, source) => { @@ -509,7 +516,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::AssignOp(op, target, value) => { bind!(self, op, target, value); kind!("AssignOp({op}, {target}, {value})"); - out!("if BinOpKind::{:?} == {op}.node;", op.value.node); + chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); self.expr(target); self.expr(value); }, @@ -573,10 +580,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Repeat({value}, {length})"); self.expr(value); match length.value { - ArrayLen::Infer(..) => out!("if let ArrayLen::Infer(..) = length;"), + ArrayLen::Infer(..) => chain!(self, "let ArrayLen::Infer(..) = length"), ArrayLen::Body(anon_const) => { bind!(self, anon_const); - out!("if let ArrayLen::Body({anon_const}) = {length};"); + chain!(self, "let ArrayLen::Body({anon_const}) = {length}"); self.body(field!(anon_const.body)); }, } @@ -600,12 +607,12 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn body(&self, body_id: &Binding) { let expr = self.cx.tcx.hir().body(body_id.value).value; bind!(self, expr); - out!("let {expr} = &cx.tcx.hir().body({body_id}).value;"); + chain!(self, "{expr} = &cx.tcx.hir().body({body_id}).value"); self.expr(expr); } fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { - let kind = |kind| out!("if let PatKind::{kind} = {pat}.kind;"); + let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -688,7 +695,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) { - let kind = |kind| out!("if let StmtKind::{kind} = {stmt}.kind;"); + let kind = |kind| chain!(self, "let StmtKind::{kind} = {stmt}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 85bcbc7ad2369..71f6c9909ddda 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,1566 +1,12 @@ -use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; -use clippy_utils::consts::{constant_simple, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::ty::match_type; -use clippy_utils::{ - def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_any_def_paths, - match_def_path, method_calls, paths, peel_blocks_with_stmt, peel_hir_expr_refs, SpanlessEq, -}; -use if_chain::if_chain; -use rustc_ast as ast; -use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId}; -use rustc_ast::visit::FnKind; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::hir_id::CRATE_HIR_ID; -use rustc_hir::intravisit::Visitor; -use rustc_hir::{ - BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, - TyKind, UnOp, -}; -use rustc_hir_analysis::hir_ty_to_ty; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::nested_filter; -use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; -use rustc_middle::ty::{ - self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, AssocKind, DefIdTree, FloatTy, Ty, -}; -use rustc_semver::RustcVersion; -use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{sym, BytePos, Span}; - -use std::borrow::{Borrow, Cow}; -use std::str; - -#[cfg(feature = "internal")] +pub mod clippy_lints_internal; +pub mod collapsible_calls; +pub mod compiler_lint_functions; +pub mod if_chain_style; +pub mod interning_defined_symbol; +pub mod invalid_paths; +pub mod lint_without_lint_pass; pub mod metadata_collector; - -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. - /// - /// ### 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. - /// - /// ### Example - /// ```rust,ignore - /// cx.span_lint(LINT_NAME, "message"); - /// ``` - /// - /// Use instead: - /// ```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. - /// - /// ### Example - /// ```rust,ignore - /// expr.span.ctxt().outer().expn_data() - /// ``` - /// - /// Use instead: - /// ```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 - /// - /// ### Example - /// ```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. - /// - /// ### Example - /// ```rust,ignore - /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } - /// ``` - /// - /// Use instead: - /// ```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. - /// - /// ### Example - /// ```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); - /// }); - /// ``` - /// - /// Use instead: - /// ```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 usages of def paths when a diagnostic item or a `LangItem` could be used. - /// - /// ### Why is this bad? - /// The path for an item is subject to change and is less efficient to look up than a - /// diagnostic item or a `LangItem`. - /// - /// ### Example - /// ```rust,ignore - /// utils::match_type(cx, ty, &paths::VEC) - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) - /// ``` - pub UNNECESSARY_DEF_PATH, - internal, - "using a def path when a diagnostic item or a `LangItem` is available" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks the paths module for invalid paths. - /// - /// ### Why is this bad? - /// It indicates a bug in the code. - /// - /// ### 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. - /// - /// ### Example - /// ```rust,ignore - /// let _ = sym!(f32); - /// ``` - /// - /// Use instead: - /// ```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 instead of strings. - /// - /// ### Example - /// ```rust,ignore - /// symbol.as_str() == "clippy"; - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// symbol == sym::clippy; - /// ``` - pub UNNECESSARY_SYMBOL_STR, - internal, - "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_clippy_lint! { - /// ### What it does - /// Checks for invalid `clippy::version` attributes. - /// - /// Valid values are: - /// * "pre 1.29.0" - /// * any valid semantic version - pub INVALID_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found an invalid `clippy::version` attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for declared clippy lints without the `clippy::version` attribute. - /// - pub MISSING_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found clippy lint without `clippy::version` attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. - /// - pub MISSING_MSRV_ATTR_IMPL, - internal, - "checking if all necessary steps were taken when adding a MSRV to a lint" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for cases of an auto-generated deprecated lint without an updated reason, - /// i.e. `"default deprecation note"`. - /// - /// ### Why is this bad? - /// Indicates that the documentation is incomplete. - /// - /// ### Example - /// ```rust,ignore - /// declare_deprecated_lint! { - /// /// ### What it does - /// /// Nothing. This lint has been deprecated. - /// /// - /// /// ### Deprecation reason - /// /// TODO - /// #[clippy::version = "1.63.0"] - /// pub COOL_LINT, - /// "default deprecation note" - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// declare_deprecated_lint! { - /// /// ### What it does - /// /// Nothing. This lint has been deprecated. - /// /// - /// /// ### Deprecation reason - /// /// This lint has been replaced by `cooler_lint` - /// #[clippy::version = "1.63.0"] - /// pub COOL_LINT, - /// "this lint has been replaced by `cooler_lint`" - /// } - /// ``` - pub DEFAULT_DEPRECATION_REASON, - internal, - "found 'default deprecation note' in a deprecated lint declaration" -} - -declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); - -impl EarlyLintPass for ClippyLintsInternal { - fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { - if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { - if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { - let mut last_name: Option<&str> = None; - for item in items { - let name = item.ident.as_str(); - if let Some(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, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]); - -impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) - || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id()) - { - return; - } - - if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { - let is_lint_ref_ty = is_lint_ref_type(cx, ty); - if is_deprecated_lint(cx, ty) || is_lint_ref_ty { - check_invalid_clippy_version_attribute(cx, item); - - let expr = &cx.tcx.hir().body(body_id).value; - let fields; - if is_lint_ref_ty { - if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind - && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind { - fields = struct_fields; - } else { - return; - } - } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind { - fields = struct_fields; - } else { - return; - } - - 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 - { - let sym_str = sym.as_str(); - if is_lint_ref_ty { - if sym_str == "default lint description" { - 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 sym_str == "default deprecation note" { - span_lint( - cx, - DEFAULT_DEPRECATION_REASON, - item.span, - &format!("the lint `{}` has the default deprecation reason", item.ident.name), - ); - } - } - } - } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { - if !matches!( - cx.tcx.item_name(macro_call.def_id).as_str(), - "impl_lint_pass" | "declare_lint_pass" - ) { - return; - } - if let hir::ItemKind::Impl(hir::Impl { - of_trait: None, - items: impl_item_refs, - .. - }) = item.kind - { - let mut collector = LintCollector { - output: &mut self.registered_lints, - cx, - }; - let body_id = cx.tcx.hir().body_owned_by( - cx.tcx.hir().local_def_id( - 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>) { - if is_lint_allowed(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 `{lint_name}` is not added to any `LintPass`"), - ); - } - } - } -} - -fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { - if let TyKind::Rptr( - _, - MutTy { - ty: 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 -} - -fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { - if let Some(value) = extract_clippy_version_value(cx, item) { - // The `sym!` macro doesn't work as it only expects a single token. - // It's better to keep it this way and have a direct `Symbol::intern` call here. - if value == Symbol::intern("pre 1.29.0") { - return; - } - - if RustcVersion::parse(value.as_str()).is_err() { - span_lint_and_help( - cx, - INVALID_CLIPPY_VERSION_ATTRIBUTE, - item.span, - "this item has an invalid `clippy::version` attribute", - None, - "please use a valid semantic version, see `doc/adding_lints.md`", - ); - } - } else { - span_lint_and_help( - cx, - MISSING_CLIPPY_VERSION_ATTRIBUTE, - item.span, - "this lint is missing the `clippy::version` attribute or version value", - None, - "please use a `clippy::version` attribute, see `doc/adding_lints.md`", - ); - } -} - -/// This function extracts the version value of a `clippy::version` attribute if the given value has -/// one -fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - attrs.iter().find_map(|attr| { - if_chain! { - // Identify attribute - if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; - if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; - if tool_name.ident.name == sym::clippy; - if attr_name.ident.name == sym::version; - if let Some(version) = attr.value_str(); - then { - Some(version) - } else { - None - } - } - }) -} - -struct LintCollector<'a, 'tcx> { - output: &'a mut FxHashSet, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { - type NestedFilter = nested_filter::All; - - 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) -> Self::Map { - 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 is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::MethodCall(path, self_arg, _, _) = &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(self_arg).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 is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) { - return; - } - - let (method_names, arg_lists, spans) = method_calls(expr, 2); - let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); - if_chain! { - if let ["expn_data", "outer_expn"] = method_names.as_slice(); - let (self_arg, args)= arg_lists[1]; - if args.is_empty(); - 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) { - assert!(!is_trigger_fn(fn_kind), "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 is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::Call(func, and_then_args) = expr.kind; - 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(&Closure { body, .. }) = &and_then_args[4].kind; - let body = cx.tcx.hir().body(body); - let only_expr = peel_blocks_with_stmt(body.value); - if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind; - if let ExprKind::Path(..) = recv.kind; - then { - let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).deny_side_effects(); - match ps.ident.as_str() { - "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - 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[0]) => { - let help_snippet = snippet(cx, span_call_args[1].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[0]) => { - let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); - }, - "help" => { - let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); - } - "note" => { - let note_snippet = snippet(cx, span_call_args[0].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[1].span, r#""...""#); - let sugg_snippet = snippet(cx, span_call_args[2].span, ".."); - let applicability_snippet = snippet(cx, span_call_args[3].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({}, {}, {}, {}, {}, {help})", - and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, - ), - 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 collapsible", - "collapse into", - format!( - "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", - and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, - ), - Applicability::MachineApplicable, - ); -} - -declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); - -#[allow(clippy::too_many_lines)] -impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - enum Item { - LangItem(Symbol), - DiagnosticItem(Symbol), - } - static PATHS: &[&[&str]] = &[ - &["clippy_utils", "match_def_path"], - &["clippy_utils", "match_trait_method"], - &["clippy_utils", "ty", "match_type"], - &["clippy_utils", "is_expr_path_def_path"], - ]; - - if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::Call(func, [cx_arg, def_arg, args@..]) = expr.kind; - if let ExprKind::Path(path) = &func.kind; - if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if let Some(which_path) = match_any_def_paths(cx, id, PATHS); - let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; - // Extract the path to the matched type - if let Some(segments) = path_to_matched_type(cx, item_arg); - let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); - if let Some(def_id) = def_path_res(cx, &segments[..], None).opt_def_id(); - then { - // def_path_res will match field names before anything else, but for this we want to match - // inherent functions first. - let def_id = if cx.tcx.def_kind(def_id) == DefKind::Field { - let method_name = *segments.last().unwrap(); - cx.tcx.def_key(def_id).parent - .and_then(|parent_idx| - cx.tcx.inherent_impls(DefId { index: parent_idx, krate: def_id.krate }).iter() - .find_map(|impl_id| cx.tcx.associated_items(*impl_id) - .find_by_name_and_kind( - cx.tcx, - Ident::from_str(method_name), - AssocKind::Fn, - *impl_id, - ) - ) - ) - .map_or(def_id, |item| item.def_id) - } else { - def_id - }; - - // Check if the target item is a diagnostic item or LangItem. - let (msg, item) = if let Some(item_name) - = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) - { - ( - "use of a def path to a diagnostic item", - Item::DiagnosticItem(*item_name), - ) - } else if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { - let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); - let item_name = cx.tcx.adt_def(lang_items).variants().iter().nth(lang_item).unwrap().name; - ( - "use of a def path to a `LangItem`", - Item::LangItem(item_name), - ) - } else { - return; - }; - - let has_ctor = match cx.tcx.def_kind(def_id) { - DefKind::Struct => { - let variant = cx.tcx.adt_def(def_id).non_enum_variant(); - variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - } - DefKind::Variant => { - let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); - variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - } - _ => false, - }; - - let mut app = Applicability::MachineApplicable; - let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); - let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); - let (sugg, with_note) = match (which_path, item) { - // match_def_path - (0, Item::DiagnosticItem(item)) => - (format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), has_ctor), - (0, Item::LangItem(item)) => ( - format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), - has_ctor - ), - // match_trait_method - (1, Item::DiagnosticItem(item)) => - (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false), - // match_type - (2, Item::DiagnosticItem(item)) => - (format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), - (2, Item::LangItem(item)) => - (format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), false), - // is_expr_path_def_path - (3, Item::DiagnosticItem(item)) if has_ctor => ( - format!( - "is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})", - ), - false, - ), - (3, Item::LangItem(item)) if has_ctor => ( - format!( - "is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})", - ), - false, - ), - (3, Item::DiagnosticItem(item)) => - (format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), - (3, Item::LangItem(item)) => ( - format!( - "path_res({cx_snip}, {def_snip}).opt_def_id()\ - .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))", - ), - false, - ), - _ => return, - }; - - span_lint_and_then( - cx, - UNNECESSARY_DEF_PATH, - expr.span, - msg, - |diag| { - diag.span_suggestion(expr.span, "try", sugg, app); - if with_note { - diag.help( - "if this `DefId` came from a constructor expression or pattern then the \ - parent `DefId` should be used instead" - ); - } - }, - ); - } - } - } -} - -fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { - match peel_hir_expr_refs(expr).0.kind { - ExprKind::Path(ref 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 { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { - path_to_matched_type(cx, init) - } else { - None - } - }, - Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( - cx, - cx.tcx.eval_static_initializer(def_id).ok()?.inner(), - cx.tcx.type_of(def_id), - ), - Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { - ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { - read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) - }, - _ => None, - }, - _ => None, - }, - ExprKind::Array(exprs) => exprs - .iter() - .map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some((*sym.as_str()).to_owned()); - } - } - - None - }) - .collect(), - _ => None, - } -} - -fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { - let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { - let &alloc = alloc.provenance().values().next()?; - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { - (alloc.inner(), ty) - } else { - return None; - } - } else { - (alloc, ty) - }; - - if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() - && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() - && ty.is_str() - { - alloc - .provenance() - .values() - .map(|&alloc| { - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { - let alloc = alloc.inner(); - str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) - .ok().map(ToOwned::to_owned) - } else { - None - } - }) - .collect() - } else { - 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 def_path_res(cx, path, None) != 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(); - // This list isn't complete, but good enough for our current list of paths. - let incoherent_impls = [ - SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), - SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), - SimplifiedTypeGen::SliceSimplifiedType, - SimplifiedTypeGen::StrSimplifiedType, - ] - .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); - for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { - let lang_item_path = cx.get_def_path(*item_def_id); - if path_syms.starts_with(&lang_item_path) { - if let [item] = &path_syms[lang_item_path.len()..] { - if matches!( - cx.tcx.def_kind(*item_def_id), - DefKind::Mod | DefKind::Enum | DefKind::Trait - ) { - for child in cx.tcx.module_children(*item_def_id) { - if child.ident.name == *item { - return true; - } - } - } else { - for child in cx.tcx.associated_item_def_ids(*item_def_id) { - if cx.tcx.item_name(*child) == *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, INVALID_PATHS, 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<'_>) { - if !self.symbol_map.is_empty() { - return; - } - - for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { - for item in cx.tcx.module_children(def_id).iter() { - if_chain! { - if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id); - 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, - ]; - 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 - }, - } - } -} - -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) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { - (cond, then, r#else.is_some()) - } else { - 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 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 let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); - 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, true).unwrap_or(span); - let span = sm.span_extend_to_next_char(span, ';', false); - Span::new( - span.lo() - BytePos(3), - span.hi() + BytePos(1), - span.ctxt(), - span.parent(), - ) -} - -declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); - -impl LateLintPass<'_> for MsrvAttrImpl { - fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if_chain! { - if let hir::ItemKind::Impl(hir::Impl { - of_trait: Some(lint_pass_trait_ref), - self_ty, - items, - .. - }) = &item.kind; - if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); - let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); - if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); - let self_ty = hir_ty_to_ty(cx.tcx, self_ty); - if let ty::Adt(self_ty_def, _) = self_ty.kind(); - if self_ty_def.is_struct(); - if self_ty_def.all_fields().any(|f| { - cx.tcx - .type_of(f.did) - .walk() - .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) - .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION)) - }); - if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); - then { - let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; - let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; - let span = cx.sess().source_map().span_through_char(item.span, '{'); - span_lint_and_sugg( - cx, - MISSING_MSRV_ATTR_IMPL, - span, - &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), - &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), - format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), - Applicability::MachineApplicable, - ); - } - } - } -} +pub mod msrv_attr_impl; +pub mod outer_expn_data_pass; +pub mod produce_ice; +pub mod unnecessary_def_path; diff --git a/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs b/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs new file mode 100644 index 0000000000000..da9514dd15eee --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs @@ -0,0 +1,49 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{Crate, ItemKind, ModKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +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. + /// + /// ### Example + /// Wrong ordering of the util::paths constants. + pub CLIPPY_LINTS_INTERNAL, + internal, + "various things that will negatively affect your clippy experience" +} + +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); + +impl EarlyLintPass for ClippyLintsInternal { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { + if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { + if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { + if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { + let mut last_name: Option<&str> = None; + for item in items { + let name = item.ident.as_str(); + if let Some(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); + } + } + } + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs new file mode 100644 index 0000000000000..d7666b77f6e96 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -0,0 +1,245 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::{Closure, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use std::borrow::{Borrow, Cow}; + +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. + /// + /// ### Example + /// ```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); + /// }); + /// ``` + /// + /// Use instead: + /// ```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_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 is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::Call(func, and_then_args) = expr.kind; + 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(&Closure { body, .. }) = &and_then_args[4].kind; + let body = cx.tcx.hir().body(body); + let only_expr = peel_blocks_with_stmt(body.value); + if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind; + if let ExprKind::Path(..) = recv.kind; + then { + let and_then_snippets = get_and_then_snippets(cx, and_then_args); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); + match ps.ident.as_str() { + "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + 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[0]) => { + let help_snippet = snippet(cx, span_call_args[1].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[0]) => { + let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); + }, + "help" => { + let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); + }, + "note" => { + let note_snippet = snippet(cx, span_call_args[0].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[1].span, r#""...""#); + let sugg_snippet = snippet(cx, span_call_args[2].span, ".."); + let applicability_snippet = snippet(cx, span_call_args[3].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({}, {}, {}, {}, {}, {help})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, + ), + 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 collapsible", + "collapse into", + format!( + "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, + ), + Applicability::MachineApplicable, + ); +} diff --git a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs new file mode 100644 index 0000000000000..cacd05262a215 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::match_type; +use clippy_utils::{is_lint_allowed, paths}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +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. + /// + /// ### Example + /// ```rust,ignore + /// cx.span_lint(LINT_NAME, "message"); + /// ``` + /// + /// Use instead: + /// ```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" +} + +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); + +#[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<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::MethodCall(path, self_arg, _, _) = &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(self_arg).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}`"), + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/if_chain_style.rs b/clippy_lints/src/utils/internal_lints/if_chain_style.rs new file mode 100644 index 0000000000000..883a5c08e5c11 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/if_chain_style.rs @@ -0,0 +1,164 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::{higher, is_else_clause, is_expn_of}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Local, Node, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Span}; + +declare_clippy_lint! { + /// Finds unidiomatic usage of `if_chain!` + pub IF_CHAIN_STYLE, + internal, + "non-idiomatic `if_chain!` usage" +} + +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) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { + (cond, then, r#else.is_some()) + } else { + return; + }; + let ExprKind::Block(then_block, _) = then.kind else { 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 Some(if_chain_span) = if_chain_span else { return }; + // check for `if a && b;` + if_chain! { + 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 let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); + 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, true).unwrap_or(span); + let span = sm.span_extend_to_next_char(span, ';', false); + Span::new( + span.lo() - BytePos(3), + span.hi() + BytePos(1), + span.ctxt(), + span.parent(), + ) +} diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs new file mode 100644 index 0000000000000..096b601572b4d --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -0,0 +1,239 @@ +use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::match_type; +use clippy_utils::{def_path_res, is_expn_of, match_def_path, paths}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::{self}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::Symbol; + +use std::borrow::Cow; + +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. + /// + /// ### Example + /// ```rust,ignore + /// let _ = sym!(f32); + /// ``` + /// + /// Use instead: + /// ```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 instead of strings. + /// + /// ### Example + /// ```rust,ignore + /// symbol.as_str() == "clippy"; + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// symbol == sym::clippy; + /// ``` + pub UNNECESSARY_SYMBOL_STR, + internal, + "unnecessary conversion between Symbol and string" +} + +#[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<'_>) { + if !self.symbol_map.is_empty() { + return; + } + + for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { + if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { + for item in cx.tcx.module_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + 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, + ]; + 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/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs new file mode 100644 index 0000000000000..25532dd4e2681 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -0,0 +1,108 @@ +use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::def_path_res; +use clippy_utils::diagnostics::span_lint; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::Item; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, FloatTy}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Symbol; + +declare_clippy_lint! { + /// ### What it does + /// Checks the paths module for invalid paths. + /// + /// ### Why is this bad? + /// It indicates a bug in the code. + /// + /// ### Example + /// None. + pub INVALID_PATHS, + internal, + "invalid path" +} + +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, INVALID_PATHS, item.span, "invalid path"); + } + } + } +} + +// 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 def_path_res(cx, path, None) != 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(); + // This list isn't complete, but good enough for our current list of paths. + let incoherent_impls = [ + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), + SimplifiedTypeGen::SliceSimplifiedType, + SimplifiedTypeGen::StrSimplifiedType, + ] + .iter() + .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); + for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { + let lang_item_path = cx.get_def_path(*item_def_id); + if path_syms.starts_with(&lang_item_path) { + if let [item] = &path_syms[lang_item_path.len()..] { + if matches!( + cx.tcx.def_kind(*item_def_id), + DefKind::Mod | DefKind::Enum | DefKind::Trait + ) { + for child in cx.tcx.module_children(*item_def_id) { + if child.ident.name == *item { + return true; + } + } + } else { + for child in cx.tcx.associated_item_def_ids(*item_def_id) { + if cx.tcx.item_name(*child) == *item { + return true; + } + } + } + } + } + } + + false +} diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs new file mode 100644 index 0000000000000..0dac64376b065 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -0,0 +1,342 @@ +use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::{is_lint_allowed, match_def_path, paths}; +use if_chain::if_chain; +use rustc_ast as ast; +use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::hir_id::CRATE_HIR_ID; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::Symbol; +use rustc_span::{sym, Span}; + +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 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. + /// + /// ### Example + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } + /// ``` + /// + /// Use instead: + /// ```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 + /// Checks for invalid `clippy::version` attributes. + /// + /// Valid values are: + /// * "pre 1.29.0" + /// * any valid semantic version + pub INVALID_CLIPPY_VERSION_ATTRIBUTE, + internal, + "found an invalid `clippy::version` attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for declared clippy lints without the `clippy::version` attribute. + /// + pub MISSING_CLIPPY_VERSION_ATTRIBUTE, + internal, + "found clippy lint without `clippy::version` attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for cases of an auto-generated deprecated lint without an updated reason, + /// i.e. `"default deprecation note"`. + /// + /// ### Why is this bad? + /// Indicates that the documentation is incomplete. + /// + /// ### Example + /// ```rust,ignore + /// declare_deprecated_lint! { + /// /// ### What it does + /// /// Nothing. This lint has been deprecated. + /// /// + /// /// ### Deprecation reason + /// /// TODO + /// #[clippy::version = "1.63.0"] + /// pub COOL_LINT, + /// "default deprecation note" + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// declare_deprecated_lint! { + /// /// ### What it does + /// /// Nothing. This lint has been deprecated. + /// /// + /// /// ### Deprecation reason + /// /// This lint has been replaced by `cooler_lint` + /// #[clippy::version = "1.63.0"] + /// pub COOL_LINT, + /// "this lint has been replaced by `cooler_lint`" + /// } + /// ``` + pub DEFAULT_DEPRECATION_REASON, + internal, + "found 'default deprecation note' in a deprecated lint declaration" +} + +#[derive(Clone, Debug, Default)] +pub struct LintWithoutLintPass { + declared_lints: FxHashMap, + registered_lints: FxHashSet, +} + +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]); + +impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) + || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id()) + { + return; + } + + if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { + let is_lint_ref_ty = is_lint_ref_type(cx, ty); + if is_deprecated_lint(cx, ty) || is_lint_ref_ty { + check_invalid_clippy_version_attribute(cx, item); + + let expr = &cx.tcx.hir().body(body_id).value; + let fields; + if is_lint_ref_ty { + if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind + && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind { + fields = struct_fields; + } else { + return; + } + } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind { + fields = struct_fields; + } else { + return; + } + + 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 + { + let sym_str = sym.as_str(); + if is_lint_ref_ty { + if sym_str == "default lint description" { + 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 sym_str == "default deprecation note" { + span_lint( + cx, + DEFAULT_DEPRECATION_REASON, + item.span, + &format!("the lint `{}` has the default deprecation reason", item.ident.name), + ); + } + } + } + } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { + if !matches!( + cx.tcx.item_name(macro_call.def_id).as_str(), + "impl_lint_pass" | "declare_lint_pass" + ) { + return; + } + if let hir::ItemKind::Impl(hir::Impl { + of_trait: None, + items: impl_item_refs, + .. + }) = item.kind + { + let mut collector = LintCollector { + output: &mut self.registered_lints, + cx, + }; + let body_id = cx.tcx.hir().body_owned_by( + cx.tcx.hir().local_def_id( + 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>) { + if is_lint_allowed(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 `{lint_name}` is not added to any `LintPass`"), + ); + } + } + } +} + +pub(super) fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { + if let TyKind::Rptr( + _, + MutTy { + ty: 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 +} + +fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { + if let Some(value) = extract_clippy_version_value(cx, item) { + // The `sym!` macro doesn't work as it only expects a single token. + // It's better to keep it this way and have a direct `Symbol::intern` call here. + if value == Symbol::intern("pre 1.29.0") { + return; + } + + if RustcVersion::parse(value.as_str()).is_err() { + span_lint_and_help( + cx, + INVALID_CLIPPY_VERSION_ATTRIBUTE, + item.span, + "this item has an invalid `clippy::version` attribute", + None, + "please use a valid semantic version, see `doc/adding_lints.md`", + ); + } + } else { + span_lint_and_help( + cx, + MISSING_CLIPPY_VERSION_ATTRIBUTE, + item.span, + "this lint is missing the `clippy::version` attribute or version value", + None, + "please use a `clippy::version` attribute, see `doc/adding_lints.md`", + ); + } +} + +/// This function extracts the version value of a `clippy::version` attribute if the given value has +/// one +pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + attrs.iter().find_map(|attr| { + if_chain! { + // Identify attribute + if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; + if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; + if tool_name.ident.name == sym::clippy; + if attr_name.ident.name == sym::version; + if let Some(version) = attr.value_str(); + then { Some(version) } else { None } + } + }) +} + +struct LintCollector<'a, 'tcx> { + output: &'a mut FxHashSet, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { + type NestedFilter = nested_filter::All; + + 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) -> Self::Map { + self.cx.tcx.hir() + } +} diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index c84191bb01034..d06a616e4b30b 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -8,7 +8,7 @@ //! a simple mistake) use crate::renamed_lints::RENAMED_LINTS; -use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type}; +use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; @@ -532,7 +532,11 @@ fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec, String)> { // Extract lints doc_comment.make_ascii_lowercase(); - let lints: Vec = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect(); + let lints: Vec = doc_comment + .split_off(DOC_START.len()) + .split(", ") + .map(str::to_string) + .collect(); // Format documentation correctly // split off leading `.` from lint name list and indent for correct formatting diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs new file mode 100644 index 0000000000000..1e994e3f2b171 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -0,0 +1,63 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +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 as hir; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::{self, subst::GenericArgKind}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. + /// + pub MISSING_MSRV_ATTR_IMPL, + internal, + "checking if all necessary steps were taken when adding a MSRV to a lint" +} + +declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); + +impl LateLintPass<'_> for MsrvAttrImpl { + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if_chain! { + if let hir::ItemKind::Impl(hir::Impl { + of_trait: Some(lint_pass_trait_ref), + self_ty, + items, + .. + }) = &item.kind; + if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); + let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); + if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); + let self_ty = hir_ty_to_ty(cx.tcx, self_ty); + if let ty::Adt(self_ty_def, _) = self_ty.kind(); + if self_ty_def.is_struct(); + if self_ty_def.all_fields().any(|f| { + cx.tcx + .type_of(f.did) + .walk() + .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) + .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION)) + }); + if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); + then { + let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; + let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; + let span = cx.sess().source_map().span_through_char(item.span, '{'); + span_lint_and_sugg( + cx, + MISSING_MSRV_ATTR_IMPL, + span, + &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), + &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), + format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs new file mode 100644 index 0000000000000..2b13fad80665c --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs @@ -0,0 +1,62 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::match_type; +use clippy_utils::{is_lint_allowed, method_calls, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Symbol; + +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. + /// + /// ### Example + /// ```rust,ignore + /// expr.span.ctxt().outer().expn_data() + /// ``` + /// + /// Use instead: + /// ```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_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 is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) { + return; + } + + let (method_names, arg_lists, spans) = method_calls(expr, 2); + let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); + if_chain! { + if let ["expn_data", "outer_expn"] = method_names.as_slice(); + let (self_arg, args) = arg_lists[1]; + if args.is_empty(); + 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, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/produce_ice.rs b/clippy_lints/src/utils/internal_lints/produce_ice.rs new file mode 100644 index 0000000000000..5899b94e16ba2 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/produce_ice.rs @@ -0,0 +1,37 @@ +use rustc_ast::ast::NodeId; +use rustc_ast::visit::FnKind; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +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 + /// + /// ### Example + /// ```rust,ignore + /// 🍦🍦🍦🍦🍦 + /// ``` + pub PRODUCE_ICE, + internal, + "this message should not appear anywhere as we ICE before and don't emit the lint" +} + +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); + +impl EarlyLintPass for ProduceIce { + fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { + assert!(!is_trigger_fn(fn_kind), "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, + } +} diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs new file mode 100644 index 0000000000000..4cf76f5362551 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -0,0 +1,343 @@ +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{def_path_res, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; +use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::Span; + +use std::str; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used. + /// + /// ### Why is this bad? + /// The path for an item is subject to change and is less efficient to look up than a + /// diagnostic item or a `LangItem`. + /// + /// ### Example + /// ```rust,ignore + /// utils::match_type(cx, ty, &paths::VEC) + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) + /// ``` + pub UNNECESSARY_DEF_PATH, + internal, + "using a def path when a diagnostic item or a `LangItem` is available" +} + +impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); + +#[derive(Default)] +pub struct UnnecessaryDefPath { + array_def_ids: FxHashSet<(DefId, Span)>, + linted_def_ids: FxHashSet, +} + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { + return; + } + + match expr.kind { + ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span), + ExprKind::Array(elements) => self.check_array(cx, elements, expr.span), + _ => {}, + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + for &(def_id, span) in &self.array_def_ids { + if self.linted_def_ids.contains(&def_id) { + continue; + } + + let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) { + ("diagnostic item", format!("sym::{sym}")) + } else if let Some(sym) = get_lang_item_name(cx, def_id) { + ("language item", format!("LangItem::{sym}")) + } else { + continue; + }; + + span_lint_and_help( + cx, + UNNECESSARY_DEF_PATH, + span, + &format!("hardcoded path to a {msg}"), + None, + &format!("convert all references to use `{sugg}`"), + ); + } + } +} + +impl UnnecessaryDefPath { + #[allow(clippy::too_many_lines)] + fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) { + enum Item { + LangItem(Symbol), + DiagnosticItem(Symbol), + } + static PATHS: &[&[&str]] = &[ + &["clippy_utils", "match_def_path"], + &["clippy_utils", "match_trait_method"], + &["clippy_utils", "ty", "match_type"], + &["clippy_utils", "is_expr_path_def_path"], + ]; + + if_chain! { + if let [cx_arg, def_arg, args @ ..] = args; + if let ExprKind::Path(path) = &func.kind; + if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if let Some(which_path) = match_any_def_paths(cx, id, PATHS); + let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; + // Extract the path to the matched type + if let Some(segments) = path_to_matched_type(cx, item_arg); + let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + if let Some(def_id) = inherent_def_path_res(cx, &segments[..]); + then { + // Check if the target item is a diagnostic item or LangItem. + #[rustfmt::skip] + let (msg, item) = if let Some(item_name) + = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) + { + ( + "use of a def path to a diagnostic item", + Item::DiagnosticItem(*item_name), + ) + } else if let Some(item_name) = get_lang_item_name(cx, def_id) { + ( + "use of a def path to a `LangItem`", + Item::LangItem(item_name), + ) + } else { + return; + }; + + let has_ctor = match cx.tcx.def_kind(def_id) { + DefKind::Struct => { + let variant = cx.tcx.adt_def(def_id).non_enum_variant(); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + }, + DefKind::Variant => { + let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + }, + _ => false, + }; + + let mut app = Applicability::MachineApplicable; + let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); + let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); + let (sugg, with_note) = match (which_path, item) { + // match_def_path + (0, Item::DiagnosticItem(item)) => ( + format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), + has_ctor, + ), + (0, Item::LangItem(item)) => ( + format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), + has_ctor, + ), + // match_trait_method + (1, Item::DiagnosticItem(item)) => { + (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) + }, + // match_type + (2, Item::DiagnosticItem(item)) => ( + format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (2, Item::LangItem(item)) => ( + format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), + false, + ), + // is_expr_path_def_path + (3, Item::DiagnosticItem(item)) if has_ctor => ( + format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), + false, + ), + (3, Item::LangItem(item)) if has_ctor => ( + format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), + false, + ), + (3, Item::DiagnosticItem(item)) => ( + format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (3, Item::LangItem(item)) => ( + format!( + "path_res({cx_snip}, {def_snip}).opt_def_id()\ + .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))", + ), + false, + ), + _ => return, + }; + + span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { + diag.span_suggestion(span, "try", sugg, app); + if with_note { + diag.help( + "if this `DefId` came from a constructor expression or pattern then the \ + parent `DefId` should be used instead", + ); + } + }); + + self.linted_def_ids.insert(def_id); + } + } + } + + fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { + let Some(path) = path_from_array(elements) else { return }; + + if let Some(def_id) = inherent_def_path_res(cx, &path.iter().map(AsRef::as_ref).collect::>()) { + self.array_def_ids.insert((def_id, span)); + } + } +} + +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { + match peel_hir_expr_refs(expr).0.kind { + ExprKind::Path(ref 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 { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { + path_to_matched_type(cx, init) + } else { + None + } + }, + Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( + cx, + cx.tcx.eval_static_initializer(def_id).ok()?.inner(), + cx.tcx.type_of(def_id), + ), + Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { + ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { + read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) + }, + _ => None, + }, + _ => None, + }, + ExprKind::Array(exprs) => path_from_array(exprs), + _ => None, + } +} + +fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { + let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { + let &alloc = alloc.provenance().values().next()?; + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + (alloc.inner(), ty) + } else { + return None; + } + } else { + (alloc, ty) + }; + + if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() + && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() + && ty.is_str() + { + alloc + .provenance() + .values() + .map(|&alloc| { + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + let alloc = alloc.inner(); + str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) + .ok().map(ToOwned::to_owned) + } else { + None + } + }) + .collect() + } else { + None + } +} + +fn path_from_array(exprs: &[Expr<'_>]) -> Option> { + exprs + .iter() + .map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some((*sym.as_str()).to_owned()); + } + } + + None + }) + .collect() +} + +// def_path_res will match field names before anything else, but for this we want to match +// inherent functions first. +fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option { + def_path_res(cx, segments, None).opt_def_id().map(|def_id| { + if cx.tcx.def_kind(def_id) == DefKind::Field { + let method_name = *segments.last().unwrap(); + cx.tcx + .def_key(def_id) + .parent + .and_then(|parent_idx| { + cx.tcx + .inherent_impls(DefId { + index: parent_idx, + krate: def_id.krate, + }) + .iter() + .find_map(|impl_id| { + cx.tcx.associated_items(*impl_id).find_by_name_and_kind( + cx.tcx, + Ident::from_str(method_name), + AssocKind::Fn, + *impl_id, + ) + }) + }) + .map_or(def_id, |item| item.def_id) + } else { + def_id + } + }) +} + +fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option { + if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { + let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); + let item_name = cx + .tcx + .adt_def(lang_items) + .variants() + .iter() + .nth(lang_item) + .unwrap() + .name; + Some(item_name) + } else { + None + } +} diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index d9b22664fd25b..cd8575c90e86c 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -136,7 +136,7 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s .emit(); }, ast::AttrStyle::Outer => { - sess.span_err(attr.span, &format!("`{name}` cannot be an outer attribute")); + sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute")); }, } } diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index fa6766f7cfe19..07e4ef6a2fef3 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -136,17 +136,49 @@ impl Constant { (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)), - (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => iter::zip(l, r) - .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) - .find(|r| r.map_or(true, |o| o != Ordering::Equal)) - .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), + (&Self::Tuple(ref l), &Self::Tuple(ref r)) if l.len() == r.len() => match *cmp_type.kind() { + ty::Tuple(tys) if tys.len() == l.len() => l + .iter() + .zip(r) + .zip(tys) + .map(|((li, ri), cmp_type)| Self::partial_cmp(tcx, cmp_type, li, ri)) + .find(|r| r.map_or(true, |o| o != Ordering::Equal)) + .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), + _ => None, + }, + (&Self::Vec(ref l), &Self::Vec(ref r)) => { + let cmp_type = match *cmp_type.kind() { + ty::Array(ty, _) | ty::Slice(ty) => ty, + _ => return None, + }; + iter::zip(l, r) + .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) + .find(|r| r.map_or(true, |o| o != Ordering::Equal)) + .unwrap_or_else(|| Some(l.len().cmp(&r.len()))) + }, (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { - match Self::partial_cmp(tcx, cmp_type, lv, rv) { + match Self::partial_cmp( + tcx, + match *cmp_type.kind() { + ty::Array(ty, _) => ty, + _ => return None, + }, + lv, + rv, + ) { Some(Equal) => Some(ls.cmp(rs)), x => x, } }, - (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb), + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp( + tcx, + match *cmp_type.kind() { + ty::Ref(_, ty, _) => ty, + _ => return None, + }, + lb, + rb, + ), // TODO: are there any useful inter-type orderings? _ => None, } diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 8724a4cd651de..95b3e651e2b53 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -120,7 +120,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .expr_ty(e) .has_significant_drop(self.cx.tcx, self.cx.param_env) { - self.eagerness = Lazy; + self.eagerness = ForceNoChange; return; } }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7e42fcc6569b6..052db3f3a0391 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -25,10 +25,12 @@ extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_typeck; +extern crate rustc_index; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; +extern crate rustc_mir_dataflow; extern crate rustc_parse_format; extern crate rustc_session; extern crate rustc_span; @@ -48,6 +50,7 @@ pub mod eager_or_lazy; pub mod higher; mod hir_utils; pub mod macros; +pub mod mir; pub mod msrvs; pub mod numeric_literal; pub mod paths; @@ -122,7 +125,7 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt return Some(version); } else if let Some(sess) = sess { if let Some(span) = span { - sess.span_err(span, &format!("`{msrv}` is not a valid Rust version")); + sess.span_err(span, format!("`{msrv}` is not a valid Rust version")); } } None @@ -815,13 +818,37 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { false } }, - ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func), + ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func), + ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg), ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), _ => false, } } +fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool { + if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind && + seg.ident.name == sym::from + { + match arg.kind { + ExprKind::Lit(hir::Lit { + node: LitKind::Str(ref sym, _), + .. + }) => return sym.is_empty() && is_path_diagnostic_item(cx, ty, sym::String), + ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), + ExprKind::Repeat(_, ArrayLen::Body(len)) => { + if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind && + let LitKind::Int(v, _) = const_lit.node + { + return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec); + } + } + _ => (), + } + } + false +} + /// Checks if the top level expression can be moved into a closure as is. /// Currently checks for: /// * Break/Continue outside the given loop HIR ids. @@ -1739,6 +1766,7 @@ pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool /// ```rust,ignore /// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX); /// ``` +/// This function is deprecated. Use [`match_function_call_with_def_id`]. pub fn match_function_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -1756,6 +1784,22 @@ pub fn match_function_call<'tcx>( None } +pub fn match_function_call_with_def_id<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + fun_def_id: DefId, +) -> Option<&'tcx [Expr<'tcx>]> { + if_chain! { + if let ExprKind::Call(fun, args) = expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id); + then { + return Some(args); + } + }; + None +} + /// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if /// any. /// diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 5a63c290a315f..9a682fbe604ff 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -627,7 +627,7 @@ pub enum Count<'tcx> { /// `FormatParamKind::Numbered`. Param(FormatParam<'tcx>), /// Not specified. - Implied, + Implied(Option), } impl<'tcx> Count<'tcx> { @@ -638,8 +638,10 @@ impl<'tcx> Count<'tcx> { inner: Option, values: &FormatArgsValues<'tcx>, ) -> Option { + let span = inner.map(|inner| span_from_inner(values.format_string_span, inner)); + Some(match count { - rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)), + rpf::Count::CountIs(val) => Self::Is(val, span?), rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new( FormatParamKind::Named(Symbol::intern(name)), usage, @@ -661,12 +663,12 @@ impl<'tcx> Count<'tcx> { inner?, values, )?), - rpf::Count::CountImplied => Self::Implied, + rpf::Count::CountImplied => Self::Implied(span), }) } pub fn is_implied(self) -> bool { - matches!(self, Count::Implied) + matches!(self, Count::Implied(_)) } pub fn param(self) -> Option> { @@ -675,6 +677,14 @@ impl<'tcx> Count<'tcx> { _ => None, } } + + pub fn span(self) -> Option { + match self { + Count::Is(_, span) => Some(span), + Count::Param(param) => Some(param.span), + Count::Implied(span) => span, + } + } } /// Specification for the formatting of an argument in the format string. See @@ -738,8 +748,13 @@ impl<'tcx> FormatSpec<'tcx> { /// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`, /// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}` pub fn is_default(&self) -> bool { - self.r#trait == sym::Display - && self.width.is_implied() + self.r#trait == sym::Display && self.is_default_for_trait() + } + + /// Has no other formatting specifiers than setting the format trait. returns true for `{}`, + /// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}` + pub fn is_default_for_trait(&self) -> bool { + self.width.is_implied() && self.precision.is_implied() && self.align == Alignment::AlignUnknown && self.flags == 0 @@ -757,6 +772,22 @@ pub struct FormatArg<'tcx> { pub span: Span, } +impl<'tcx> FormatArg<'tcx> { + /// Span of the `:` and format specifiers + /// + /// ```ignore + /// format!("{:.}"), format!("{foo:.}") + /// ^^ ^^ + /// ``` + pub fn format_span(&self) -> Span { + let base = self.span.data(); + + // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing + // brace `{...|}` + Span::new(self.param.span.hi(), base.hi - BytePos(1), base.ctxt, base.parent) + } +} + /// A parsed `format_args!` expansion. #[derive(Debug)] pub struct FormatArgsExpn<'tcx> { diff --git a/clippy_utils/src/mir/maybe_storage_live.rs b/clippy_utils/src/mir/maybe_storage_live.rs new file mode 100644 index 0000000000000..d262b335d99d3 --- /dev/null +++ b/clippy_utils/src/mir/maybe_storage_live.rs @@ -0,0 +1,52 @@ +use rustc_index::bit_set::BitSet; +use rustc_middle::mir; +use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis}; + +/// Determines liveness of each local purely based on `StorageLive`/`Dead`. +#[derive(Copy, Clone)] +pub(super) struct MaybeStorageLive; + +impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { + type Domain = BitSet; + const NAME: &'static str = "maybe_storage_live"; + + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) + } + + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { + for arg in body.args_iter() { + state.insert(arg); + } + } +} + +impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { + type Idx = mir::Local; + + fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { + match stmt.kind { + mir::StatementKind::StorageLive(l) => trans.gen(l), + mir::StatementKind::StorageDead(l) => trans.kill(l), + _ => (), + } + } + + fn terminator_effect( + &self, + _trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + _loc: mir::Location, + ) { + } + + fn call_return_effect( + &self, + _trans: &mut impl GenKill, + _block: mir::BasicBlock, + _return_places: CallReturnPlaces<'_, 'tcx>, + ) { + // Nothing to do when a call returns successfully + } +} diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs new file mode 100644 index 0000000000000..818e603f665e7 --- /dev/null +++ b/clippy_utils/src/mir/mod.rs @@ -0,0 +1,164 @@ +use rustc_hir::{Expr, HirId}; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{ + traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, +}; +use rustc_middle::ty::TyCtxt; + +mod maybe_storage_live; + +mod possible_borrower; +pub use possible_borrower::PossibleBorrowerMap; + +mod possible_origin; + +mod transitive_relation; + +#[derive(Clone, Debug, Default)] +pub struct LocalUsage { + /// The locations where the local is used, if any. + pub local_use_locs: Vec, + /// The locations where the local is consumed or mutated, if any. + pub local_consume_or_mutate_locs: Vec, +} + +pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -> Option> { + let init = vec![ + LocalUsage { + local_use_locs: Vec::new(), + local_consume_or_mutate_locs: Vec::new(), + }; + locals.len() + ]; + + traversal::ReversePostorder::new(mir, location.block).try_fold(init, |usage, (tbb, tdata)| { + // Give up on loops + if tdata.terminator().successors().any(|s| s == location.block) { + return None; + } + + let mut v = V { + locals, + location, + results: usage, + }; + v.visit_basic_block_data(tbb, tdata); + Some(v.results) + }) +} + +struct V<'a> { + locals: &'a [Local], + location: Location, + results: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for V<'a> { + fn visit_place(&mut self, place: &Place<'tcx>, ctx: PlaceContext, loc: Location) { + if loc.block == self.location.block && loc.statement_index <= self.location.statement_index { + return; + } + + let local = place.local; + + for (i, self_local) in self.locals.iter().enumerate() { + if local == *self_local { + if !matches!( + ctx, + PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) + ) { + self.results[i].local_use_locs.push(loc); + } + if matches!( + ctx, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) + ) { + self.results[i].local_consume_or_mutate_locs.push(loc); + } + } + } + } +} + +/// Convenience wrapper around `visit_local_usage`. +pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option { + visit_local_usage( + &[local], + mir, + Location { + block: START_BLOCK, + statement_index: 0, + }, + ) + .map(|mut vec| { + let LocalUsage { local_use_locs, .. } = vec.remove(0); + local_use_locs + .into_iter() + .filter(|location| !is_local_assignment(mir, local, *location)) + .count() + == 1 + }) +} + +/// Returns the `mir::Body` containing the node associated with `hir_id`. +#[allow(clippy::module_name_repetitions)] +pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> &Body<'_> { + let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id); + tcx.optimized_mir(body_owner_local_def_id.to_def_id()) +} + +/// Tries to determine the `Local` corresponding to `expr`, if any. +/// This function is expensive and should be used sparingly. +pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option { + let mir = enclosing_mir(tcx, expr.hir_id); + mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| { + if local_decl.source_info.span == expr.span { + Some(local) + } else { + None + } + }) +} + +/// Returns a vector of `mir::Location` where `local` is assigned. +pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec { + let mut locations = Vec::new(); + for (block, data) in mir.basic_blocks.iter_enumerated() { + for statement_index in 0..=data.statements.len() { + let location = Location { block, statement_index }; + if is_local_assignment(mir, local, location) { + locations.push(location); + } + } + } + locations +} + +// `is_local_assignment` is based on `is_place_assignment`: +// https://github.com/rust-lang/rust/blob/b7413511dc85ec01ef4b91785f86614589ac6103/compiler/rustc_middle/src/mir/visit.rs#L1350 +fn is_local_assignment(mir: &Body<'_>, local: Local, location: Location) -> bool { + let Location { block, statement_index } = location; + let basic_block = &mir.basic_blocks[block]; + if statement_index < basic_block.statements.len() { + let statement = &basic_block.statements[statement_index]; + if let StatementKind::Assign(box (place, _)) = statement.kind { + place.as_local() == Some(local) + } else { + false + } + } else { + let terminator = basic_block.terminator(); + match &terminator.kind { + TerminatorKind::Call { destination, .. } => destination.as_local() == Some(local), + TerminatorKind::InlineAsm { operands, .. } => operands.iter().any(|operand| { + if let InlineAsmOperand::Out { place: Some(place), .. } = operand { + place.as_local() == Some(local) + } else { + false + } + }), + _ => false, + } + } +} diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs new file mode 100644 index 0000000000000..25717bf3d2fee --- /dev/null +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -0,0 +1,241 @@ +use super::{ + maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor, + transitive_relation::TransitiveRelation, +}; +use crate::ty::is_copy; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_lint::LateContext; +use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; +use rustc_middle::ty::{self, visit::TypeVisitor}; +use rustc_mir_dataflow::{Analysis, ResultsCursor}; +use std::ops::ControlFlow; + +/// Collects the possible borrowers of each local. +/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` +/// possible borrowers of `a`. +#[allow(clippy::module_name_repetitions)] +struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { + possible_borrower: TransitiveRelation, + body: &'b mir::Body<'tcx>, + cx: &'a LateContext<'tcx>, + possible_origin: FxHashMap>, +} + +impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn new( + cx: &'a LateContext<'tcx>, + body: &'b mir::Body<'tcx>, + possible_origin: FxHashMap>, + ) -> Self { + Self { + possible_borrower: TransitiveRelation::default(), + cx, + body, + possible_origin, + } + } + + fn into_map( + self, + cx: &'a LateContext<'tcx>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + ) -> PossibleBorrowerMap<'b, 'tcx> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } + + let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); + borrowers.remove(mir::Local::from_usize(0)); + if !borrowers.is_empty() { + map.insert(row, borrowers); + } + } + + let bs = BitSet::new_empty(self.body.local_decls.len()); + PossibleBorrowerMap { + map, + maybe_live, + bitset: (bs.clone(), bs), + } + } +} + +impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + mir::Rvalue::Ref(_, _, borrowed) => { + self.possible_borrower.add(borrowed.local, lhs); + }, + other => { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) + .is_continue() + { + return; + } + rvalue_locals(other, |rhs| { + if lhs != rhs { + self.possible_borrower.add(rhs, lhs); + } + }); + }, + } + } + + fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { + if let mir::TerminatorKind::Call { + args, + destination: mir::Place { local: dest, .. }, + .. + } = &terminator.kind + { + // TODO add doc + // 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`. + + let mut immutable_borrowers = vec![]; + let mut mutable_borrowers = vec![]; + + for op in args { + match op { + mir::Operand::Copy(p) | mir::Operand::Move(p) => { + if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() { + mutable_borrowers.push(p.local); + } else { + immutable_borrowers.push(p.local); + } + }, + mir::Operand::Constant(..) => (), + } + } + + let mut mutable_variables: Vec = mutable_borrowers + .iter() + .filter_map(|r| self.possible_origin.get(r)) + .flat_map(HybridBitSet::iter) + .collect(); + + if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { + mutable_variables.push(*dest); + } + + for y in mutable_variables { + for x in &immutable_borrowers { + self.possible_borrower.add(*x, y); + } + for x in &mutable_borrowers { + self.possible_borrower.add(*x, y); + } + } + } + } +} + +struct ContainsRegion; + +impl TypeVisitor<'_> for ContainsRegion { + type BreakTy = (); + + fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow { + ControlFlow::BREAK + } +} + +fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { + use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; + + 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 { + Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), + Aggregate(_, ops) => ops.iter().for_each(visit_op), + BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { + visit_op(lhs); + visit_op(rhs); + }, + _ => (), + } +} + +/// Result of `PossibleBorrowerVisitor`. +#[allow(clippy::module_name_repetitions)] +pub struct PossibleBorrowerMap<'b, 'tcx> { + /// Mapping `Local -> its possible borrowers` + pub map: FxHashMap>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + // Caches to avoid allocation of `BitSet` on every query + pub bitset: (BitSet, BitSet), +} + +impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { + pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { + let possible_origin = { + let mut vis = PossibleOriginVisitor::new(mir); + vis.visit_body(mir); + vis.into_map(cx) + }; + let maybe_storage_live_result = MaybeStorageLive + .into_engine(cx.tcx, mir) + .pass_name("redundant_clone") + .iterate_to_fixpoint() + .into_results_cursor(mir); + let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); + vis.visit_body(mir); + vis.into_map(cx, maybe_storage_live_result) + } + + /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. + pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { + self.bounded_borrowers(borrowers, borrowers, borrowed, at) + } + + /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below` + /// but no more than `above`. + pub fn bounded_borrowers( + &mut self, + below: &[mir::Local], + above: &[mir::Local], + borrowed: mir::Local, + at: mir::Location, + ) -> bool { + self.maybe_live.seek_after_primary_effect(at); + + self.bitset.0.clear(); + let maybe_live = &mut self.maybe_live; + if let Some(bitset) = self.map.get(&borrowed) { + for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { + self.bitset.0.insert(b); + } + } else { + return false; + } + + self.bitset.1.clear(); + for b in below { + self.bitset.1.insert(*b); + } + + if !self.bitset.0.superset(&self.bitset.1) { + return false; + } + + for b in above { + self.bitset.0.remove(*b); + } + + self.bitset.0.is_empty() + } + + pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { + self.maybe_live.seek_after_primary_effect(at); + self.maybe_live.contains(local) + } +} diff --git a/clippy_utils/src/mir/possible_origin.rs b/clippy_utils/src/mir/possible_origin.rs new file mode 100644 index 0000000000000..8e7513d740ab3 --- /dev/null +++ b/clippy_utils/src/mir/possible_origin.rs @@ -0,0 +1,59 @@ +use super::transitive_relation::TransitiveRelation; +use crate::ty::is_copy; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::HybridBitSet; +use rustc_lint::LateContext; +use rustc_middle::mir; + +/// Collect possible borrowed for every `&mut` local. +/// For example, `_1 = &mut _2` generate _1: {_2,...} +/// Known Problems: not sure all borrowed are tracked +#[allow(clippy::module_name_repetitions)] +pub(super) struct PossibleOriginVisitor<'a, 'tcx> { + possible_origin: TransitiveRelation, + body: &'a mir::Body<'tcx>, +} + +impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { + pub fn new(body: &'a mir::Body<'tcx>) -> Self { + Self { + possible_origin: TransitiveRelation::default(), + body, + } + } + + pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } + + let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len()); + borrowers.remove(mir::Local::from_usize(0)); + if !borrowers.is_empty() { + map.insert(row, borrowers); + } + } + map + } +} + +impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + // Only consider `&mut`, which can modify origin place + mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) | + // _2: &mut _; + // _3 = move _2 + mir::Rvalue::Use(mir::Operand::Move(borrowed)) | + // _3 = move _2 as &mut _; + mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _) + => { + self.possible_origin.add(lhs, borrowed.local); + }, + _ => {}, + } + } +} diff --git a/clippy_utils/src/mir/transitive_relation.rs b/clippy_utils/src/mir/transitive_relation.rs new file mode 100644 index 0000000000000..7fe2960739fa2 --- /dev/null +++ b/clippy_utils/src/mir/transitive_relation.rs @@ -0,0 +1,29 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::HybridBitSet; +use rustc_middle::mir; + +#[derive(Default)] +pub(super) struct TransitiveRelation { + relations: FxHashMap>, +} + +impl TransitiveRelation { + pub fn add(&mut self, a: mir::Local, b: mir::Local) { + self.relations.entry(a).or_default().push(b); + } + + pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet { + let mut seen = HybridBitSet::new_empty(domain_size); + let mut stack = vec![a]; + while let Some(u) = stack.pop() { + if let Some(edges) = self.relations.get(&u) { + for &v in edges { + if seen.insert(v) { + stack.push(v); + } + } + } + } + seen + } +} diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 80098d9766c67..c5dcd7b31f58e 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -69,12 +69,13 @@ impl<'a> NumericLiteral<'a> { #[must_use] pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self { + let unsigned_lit = lit.trim_start_matches('-'); // Determine delimiter for radix prefix, if present, and radix. - let radix = if lit.starts_with("0x") { + let radix = if unsigned_lit.starts_with("0x") { Radix::Hexadecimal - } else if lit.starts_with("0b") { + } else if unsigned_lit.starts_with("0b") { Radix::Binary - } else if lit.starts_with("0o") { + } else if unsigned_lit.starts_with("0o") { Radix::Octal } else { Radix::Decimal diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 13938645fc3e5..bc85147343045 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -16,25 +16,17 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ #[cfg(feature = "internal")] pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; -pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; -pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; 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_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; -pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"]; pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"]; pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"]; pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"]; -pub const CORE_ITER_INTO_ITER: [&str; 6] = ["core", "iter", "traits", "collect", "IntoIterator", "into_iter"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"]; pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; -/// Preferably use the diagnostic item `sym::deref_method` where possible -pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; -pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; #[cfg(feature = "internal")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; #[cfg(feature = "internal")] @@ -42,30 +34,22 @@ pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"] pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; pub const F64_EPSILON: [&str; 4] = ["core", "f64", "", "EPSILON"]; -pub const FILE: [&str; 3] = ["std", "fs", "File"]; -pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; -pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; -pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; pub const 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_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"]; #[cfg(feature = "internal")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; #[cfg(feature = "internal")] pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; -pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; -pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; -pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; @@ -76,13 +60,7 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; #[cfg(feature = "internal")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"]; -pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; -/// Preferably use the diagnostic item `sym::Option` where possible -pub const OPTION: [&str; 3] = ["core", "option", "Option"]; -pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"]; -pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"]; -pub const ORD: [&str; 3] = ["core", "cmp", "Ord"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"]; @@ -95,8 +73,6 @@ pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; #[cfg_attr(not(unix), allow(clippy::invalid_paths))] pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; -pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; -pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"]; pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; @@ -119,26 +95,14 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; -/// Preferably use the diagnostic item `sym::Result` where possible -pub const RESULT: [&str; 3] = ["core", "result", "Result"]; -pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"]; -pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"]; #[cfg(feature = "internal")] pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"]; -pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"]; -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"]; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 5089987ef7200..aad7da61a8a54 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -1,7 +1,9 @@ //! Contains utility functions to generate suggestions. #![deny(clippy::missing_docs_in_private_items)] -use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite}; +use crate::source::{ + snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite, +}; use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; use rustc_ast::util::parser::AssocOp; @@ -110,7 +112,7 @@ impl<'a> Sugg<'a> { if expr.span.ctxt() == ctxt { Self::hir_from_snippet(expr, |span| snippet(cx, span, default)) } else { - let snip = snippet_with_applicability(cx, expr.span, default, applicability); + let (snip, _) = snippet_with_context(cx, expr.span, ctxt, default, applicability); Sugg::NonParen(snip) } } @@ -1052,12 +1054,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read( - &mut self, - _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) {} + fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } #[cfg(test)] diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index b344db634f612..b8824024e6c78 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -73,8 +73,7 @@ impl LintcheckConfig { let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { clap_config .get_one::("crates-toml") - .map(|s| &**s) - .unwrap_or("lintcheck/lintcheck_crates.toml") + .map_or("lintcheck/lintcheck_crates.toml", |s| &**s) .into() }); @@ -97,7 +96,7 @@ impl LintcheckConfig { Some(&0) => { // automatic choice // Rayon seems to return thread count so half that for core count - (rayon::current_num_threads() / 2) as usize + rayon::current_num_threads() / 2 }, Some(&threads) => threads, // no -j passed, use a single thread diff --git a/lintcheck/src/driver.rs b/lintcheck/src/driver.rs index 63221bab32d31..47724a2fedb00 100644 --- a/lintcheck/src/driver.rs +++ b/lintcheck/src/driver.rs @@ -5,7 +5,7 @@ use std::net::TcpStream; use std::process::{self, Command, Stdio}; use std::{env, mem}; -/// 1. Sends [DriverInfo] to the [crate::recursive::LintcheckServer] running on `addr` +/// 1. Sends [`DriverInfo`] to the [`crate::recursive::LintcheckServer`] running on `addr` /// 2. Receives [bool] from the server, if `false` returns `None` /// 3. Otherwise sends the stderr of running `clippy-driver` to the server fn run_clippy(addr: &str) -> Option { diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index cc2b3e1acec71..54c1b80c42dbf 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -116,12 +116,13 @@ impl ClippyWarning { let span = diag.spans.into_iter().find(|span| span.is_primary)?; - let file = match Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { - Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()), - Err(_) => format!( + let file = if let Ok(stripped) = Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { + format!("$CARGO_HOME/{}", stripped.display()) + } else { + format!( "target/lintcheck/sources/{}-{}/{}", crate_name, crate_version, span.file_name - ), + ) }; Some(Self { @@ -144,16 +145,17 @@ impl ClippyWarning { } let mut output = String::from("| "); - let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line); + let _ = write!(output, "[`{file_with_pos}`]({file}#L{})", self.line); let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message); output.push('\n'); output } else { - format!("{} {} \"{}\"\n", file_with_pos, self.lint_type, self.message) + format!("{file_with_pos} {} \"{}\"\n", self.lint_type, self.message) } } } +#[allow(clippy::result_large_err)] fn get(path: &str) -> Result { const MAX_RETRIES: u8 = 4; let mut retries = 0; @@ -161,11 +163,11 @@ fn get(path: &str) -> Result { match ureq::get(path).call() { Ok(res) => return Ok(res), Err(e) if retries >= MAX_RETRIES => return Err(e), - Err(ureq::Error::Transport(e)) => eprintln!("Error: {}", e), + Err(ureq::Error::Transport(e)) => eprintln!("Error: {e}"), Err(e) => return Err(e), } - eprintln!("retrying in {} seconds...", retries); - thread::sleep(Duration::from_secs(retries as u64)); + eprintln!("retrying in {retries} seconds..."); + thread::sleep(Duration::from_secs(u64::from(retries))); retries += 1; } } @@ -181,11 +183,11 @@ impl CrateSource { let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS); // url to download the crate from crates.io - let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version); - println!("Downloading and extracting {} {} from {}", name, version, url); + let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download"); + println!("Downloading and extracting {name} {version} from {url}"); create_dirs(&krate_download_dir, &extract_dir); - let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version)); + let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz")); // don't download/extract if we already have done so if !krate_file_path.is_file() { // create a file path to download and write the crate data into @@ -205,7 +207,7 @@ impl CrateSource { Crate { version: version.clone(), name: name.clone(), - path: extract_dir.join(format!("{}-{}/", name, version)), + path: extract_dir.join(format!("{name}-{version}/")), options: options.clone(), } }, @@ -218,12 +220,12 @@ impl CrateSource { let repo_path = { let mut repo_path = PathBuf::from(LINTCHECK_SOURCES); // add a -git suffix in case we have the same crate from crates.io and a git repo - repo_path.push(format!("{}-git", name)); + repo_path.push(format!("{name}-git")); repo_path }; // clone the repo if we have not done so if !repo_path.is_dir() { - println!("Cloning {} and checking out {}", url, commit); + println!("Cloning {url} and checking out {commit}"); if !Command::new("git") .arg("clone") .arg(url) @@ -232,7 +234,7 @@ impl CrateSource { .expect("Failed to clone git repo!") .success() { - eprintln!("Failed to clone {} into {}", url, repo_path.display()) + eprintln!("Failed to clone {url} into {}", repo_path.display()); } } // check out the commit/branch/whatever @@ -245,7 +247,7 @@ impl CrateSource { .expect("Failed to check out commit") .success() { - eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display()) + eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display()); } Crate { @@ -256,22 +258,22 @@ impl CrateSource { } }, CrateSource::Path { name, path, options } => { + fn is_cache_dir(entry: &DirEntry) -> bool { + std::fs::read(entry.path().join("CACHEDIR.TAG")) + .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) + .unwrap_or(false) + } + // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file. // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory // as a result of this filter. let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name); if dest_crate_root.exists() { - println!("Deleting existing directory at {:?}", dest_crate_root); + println!("Deleting existing directory at {dest_crate_root:?}"); std::fs::remove_dir_all(&dest_crate_root).unwrap(); } - println!("Copying {:?} to {:?}", path, dest_crate_root); - - fn is_cache_dir(entry: &DirEntry) -> bool { - std::fs::read(entry.path().join("CACHEDIR.TAG")) - .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) - .unwrap_or(false) - } + println!("Copying {path:?} to {dest_crate_root:?}"); for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) { let entry = entry.unwrap(); @@ -301,6 +303,7 @@ impl CrateSource { impl Crate { /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy /// issued + #[allow(clippy::too_many_arguments)] fn run_clippy_lints( &self, cargo_clippy_path: &Path, @@ -345,14 +348,14 @@ impl Crate { clippy_args.push(opt); } } else { - clippy_args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) + clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]); } if lint_filter.is_empty() { clippy_args.push("--cap-lints=warn"); } else { clippy_args.push("--cap-lints=allow"); - clippy_args.extend(lint_filter.iter().map(|filter| filter.as_str())) + clippy_args.extend(lint_filter.iter().map(std::string::String::as_str)); } if let Some(server) = server { @@ -389,10 +392,7 @@ impl Crate { let all_output = Command::new(&cargo_clippy_path) // use the looping index to create individual target dirs - .env( - "CARGO_TARGET_DIR", - shared_target_dir.join(format!("_{:?}", thread_index)), - ) + .env("CARGO_TARGET_DIR", shared_target_dir.join(format!("_{thread_index:?}"))) .args(&cargo_clippy_args) .current_dir(&self.path) .output() @@ -422,8 +422,8 @@ impl Crate { { let subcrate = &stderr[63..]; println!( - "ERROR: failed to apply some suggetion to {} / to (sub)crate {}", - self.name, subcrate + "ERROR: failed to apply some suggetion to {} / to (sub)crate {subcrate}", + self.name ); } // fast path, we don't need the warnings anyway @@ -457,20 +457,16 @@ fn build_clippy() { /// Read a `lintcheck_crates.toml` file fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = - std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); + std::fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = - toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e)); + toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display())); // parse the hashmap of the toml file into a list of crates - let tomlcrates: Vec = crate_list - .crates - .into_iter() - .map(|(_cratename, tomlcrate)| tomlcrate) - .collect(); + let tomlcrates: Vec = crate_list.crates.into_values().collect(); // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => // multiple Cratesources) let mut crate_sources = Vec::new(); - tomlcrates.into_iter().for_each(|tk| { + for tk in tomlcrates { if let Some(ref path) = tk.path { crate_sources.push(CrateSource::Path { name: tk.name.clone(), @@ -479,13 +475,13 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { }); } else if let Some(ref versions) = tk.versions { // if we have multiple versions, save each one - versions.iter().for_each(|ver| { + for ver in versions.iter() { crate_sources.push(CrateSource::CratesIo { name: tk.name.clone(), version: ver.to_string(), options: tk.options.clone(), }); - }) + } } else if tk.git_url.is_some() && tk.git_hash.is_some() { // otherwise, we should have a git source crate_sources.push(CrateSource::Git { @@ -502,16 +498,19 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some()) || tk.git_hash.is_some() != tk.git_url.is_some() { - eprintln!("tomlkrate: {:?}", tk); - if tk.git_hash.is_some() != tk.git_url.is_some() { - panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!"); - } - if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) { - panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields"); - } + eprintln!("tomlkrate: {tk:?}"); + assert_eq!( + tk.git_hash.is_some(), + tk.git_url.is_some(), + "Error: Encountered TomlCrate with only one of git_hash and git_url!" + ); + assert!( + tk.path.is_none() || (tk.git_hash.is_none() && tk.versions.is_none()), + "Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields" + ); unreachable!("Failed to translate TomlCrate into CrateSource!"); } - }); + } // sort the crates crate_sources.sort(); @@ -530,13 +529,13 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect(); // sort by "000{count} {clippy::lintname}" // to not have a lint with 200 and 2 warnings take the same spot - stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint)); + stats.sort_by_key(|(lint, count)| format!("{count:0>4}, {lint}")); let mut header = String::from("| lint | count |\n"); header.push_str("| -------------------------------------------------- | ----- |\n"); let stats_string = stats .iter() - .map(|(lint, count)| format!("| {:<50} | {:>4} |\n", lint, count)) + .map(|(lint, count)| format!("| {lint:<50} | {count:>4} |\n")) .fold(header, |mut table, line| { table.push_str(&line); table @@ -573,6 +572,7 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path, paths: [&Path; 2]) -> bool logs_modified < clippy_modified } +#[allow(clippy::too_many_lines)] fn main() { // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive` if let Ok(addr) = env::var("LINTCHECK_SERVER") { @@ -602,10 +602,10 @@ fn main() { ) { let shared_target_dir = "target/lintcheck/shared_target_dir"; // if we get an Err here, the shared target dir probably does simply not exist - if let Ok(metadata) = std::fs::metadata(&shared_target_dir) { + if let Ok(metadata) = std::fs::metadata(shared_target_dir) { if metadata.is_dir() { println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir..."); - std::fs::remove_dir_all(&shared_target_dir) + std::fs::remove_dir_all(shared_target_dir) .expect("failed to remove target/lintcheck/shared_target_dir"); } } @@ -678,7 +678,7 @@ fn main() { .unwrap(); let server = config.recursive.then(|| { - let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive"); + fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive").unwrap_or_default(); LintcheckServer::spawn(recursive_options) }); @@ -734,8 +734,8 @@ fn main() { } write!(text, "{}", all_msgs.join("")).unwrap(); text.push_str("\n\n### ICEs:\n"); - for (cratename, msg) in ices.iter() { - let _ = write!(text, "{}: '{}'", cratename, msg); + for (cratename, msg) in &ices { + let _ = write!(text, "{cratename}: '{msg}'"); } println!("Writing logs to {}", config.lintcheck_results_path.display()); @@ -779,7 +779,7 @@ fn read_stats_from_file(file_path: &Path) -> HashMap { fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, usize>, lint_filter: &Vec) { let same_in_both_hashmaps = old_stats .iter() - .filter(|(old_key, old_val)| new_stats.get::<&String>(&old_key) == Some(old_val)) + .filter(|(old_key, old_val)| new_stats.get::<&String>(old_key) == Some(old_val)) .map(|(k, v)| (k.to_string(), *v)) .collect::>(); @@ -787,37 +787,37 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us let mut new_stats_deduped = new_stats; // remove duplicates from both hashmaps - same_in_both_hashmaps.iter().for_each(|(k, v)| { + for (k, v) in &same_in_both_hashmaps { assert!(old_stats_deduped.remove(k) == Some(*v)); assert!(new_stats_deduped.remove(k) == Some(*v)); - }); + } println!("\nStats:"); // list all new counts (key is in new stats but not in old stats) new_stats_deduped .iter() - .filter(|(new_key, _)| old_stats_deduped.get::(&new_key).is_none()) + .filter(|(new_key, _)| old_stats_deduped.get::(new_key).is_none()) .for_each(|(new_key, new_value)| { - println!("{} 0 => {}", new_key, new_value); + println!("{new_key} 0 => {new_value}"); }); // list all changed counts (key is in both maps but value differs) new_stats_deduped .iter() - .filter(|(new_key, _new_val)| old_stats_deduped.get::(&new_key).is_some()) + .filter(|(new_key, _new_val)| old_stats_deduped.get::(new_key).is_some()) .for_each(|(new_key, new_val)| { - let old_val = old_stats_deduped.get::(&new_key).unwrap(); - println!("{} {} => {}", new_key, old_val, new_val); + let old_val = old_stats_deduped.get::(new_key).unwrap(); + println!("{new_key} {old_val} => {new_val}"); }); // list all gone counts (key is in old status but not in new stats) old_stats_deduped .iter() - .filter(|(old_key, _)| new_stats_deduped.get::<&String>(&old_key).is_none()) + .filter(|(old_key, _)| new_stats_deduped.get::<&String>(old_key).is_none()) .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key)) .for_each(|(old_key, old_value)| { - println!("{} {} => 0", old_key, old_value); + println!("{old_key} {old_value} => 0"); }); } @@ -828,19 +828,21 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us /// This function panics if creating one of the dirs fails. fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create lintcheck target dir"); - } + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create lintcheck target dir" + ); }); - std::fs::create_dir(&krate_download_dir).unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create crate download dir"); - } + std::fs::create_dir(krate_download_dir).unwrap_or_else(|err| { + assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir"); }); - std::fs::create_dir(&extract_dir).unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create crate extraction dir"); - } + std::fs::create_dir(extract_dir).unwrap_or_else(|err| { + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create crate extraction dir" + ); }); } @@ -863,7 +865,7 @@ fn lintcheck_test() { "lintcheck/test_sources.toml", ]; let status = std::process::Command::new("cargo") - .args(&args) + .args(args) .current_dir("..") // repo root .status(); //.output(); diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs index 67dcfc2b199c4..49072e65192f2 100644 --- a/lintcheck/src/recursive.rs +++ b/lintcheck/src/recursive.rs @@ -1,7 +1,7 @@ //! In `--recursive` mode we set the `lintcheck` binary as the `RUSTC_WRAPPER` of `cargo check`, -//! this allows [crate::driver] to be run for every dependency. The driver connects to -//! [LintcheckServer] to ask if it should be skipped, and if not sends the stderr of running clippy -//! on the crate to the server +//! this allows [`crate::driver`] to be run for every dependency. The driver connects to +//! [`LintcheckServer`] to ask if it should be skipped, and if not sends the stderr of running +//! clippy on the crate to the server use crate::ClippyWarning; use crate::RecursiveOptions; @@ -109,8 +109,8 @@ impl LintcheckServer { Self { local_addr, - sender, receiver, + sender, } } diff --git a/rust-toolchain b/rust-toolchain index 49b13cb54e71e..748d8a317160f 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-10-06" +channel = "nightly-2022-10-20" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/docs.rs b/src/docs.rs index bd27bc7938f8e..c033ad294a38d 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -28,6 +28,7 @@ docs! { "approx_constant", "arithmetic_side_effects", "as_conversions", + "as_ptr_cast_mut", "as_underscore", "assertions_on_constants", "assertions_on_result_states", @@ -60,6 +61,7 @@ docs! { "cast_enum_constructor", "cast_enum_truncation", "cast_lossless", + "cast_nan_to_int", "cast_possible_truncation", "cast_possible_wrap", "cast_precision_loss", @@ -257,6 +259,7 @@ docs! { "manual_async_fn", "manual_bits", "manual_clamp", + "manual_filter", "manual_filter_map", "manual_find", "manual_find_map", @@ -313,6 +316,7 @@ docs! { "missing_panics_doc", "missing_safety_doc", "missing_spin_loop", + "missing_trait_methods", "mistyped_literal_suffixes", "mixed_case_hex_literals", "mixed_read_write_in_expression", @@ -391,6 +395,7 @@ docs! { "panic", "panic_in_result_fn", "panicking_unwrap", + "partial_pub_fields", "partialeq_ne_impl", "partialeq_to_none", "path_buf_push_overwrite", @@ -553,6 +558,7 @@ docs! { "unseparated_literal_suffix", "unsound_collection_transmute", "unused_async", + "unused_format_specs", "unused_io_amount", "unused_peekable", "unused_rounding", diff --git a/src/docs/as_ptr_cast_mut.txt b/src/docs/as_ptr_cast_mut.txt new file mode 100644 index 0000000000000..228dde996bb2f --- /dev/null +++ b/src/docs/as_ptr_cast_mut.txt @@ -0,0 +1,19 @@ +### What it does +Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer + +### Why is this bad? +Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior +mutability is used, making it unlikely that having it as a mutable pointer is correct. + +### Example +``` +let string = String::with_capacity(1); +let ptr = string.as_ptr() as *mut u8; +unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR +``` +Use instead: +``` +let mut string = String::with_capacity(1); +let ptr = string.as_mut_ptr(); +unsafe { ptr.write(4) }; +``` \ No newline at end of file diff --git a/src/docs/box_default.txt b/src/docs/box_default.txt index ffac894d0c50a..1c670c7733377 100644 --- a/src/docs/box_default.txt +++ b/src/docs/box_default.txt @@ -7,12 +7,6 @@ First, it's more complex, involving two calls instead of one. Second, `Box::default()` can be faster [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). -### Known problems -The lint may miss some cases (e.g. Box::new(String::from(""))). -On the other hand, it will trigger on cases where the `default` -code comes from a macro that does something different based on -e.g. target operating system. - ### Example ``` let x: Box = Box::new(Default::default()); diff --git a/src/docs/cast_nan_to_int.txt b/src/docs/cast_nan_to_int.txt new file mode 100644 index 0000000000000..122f5da0c9218 --- /dev/null +++ b/src/docs/cast_nan_to_int.txt @@ -0,0 +1,15 @@ +### What it does +Checks for a known NaN float being cast to an integer + +### Why is this bad? +NaNs are cast into zero, so one could simply use this and make the +code more readable. The lint could also hint at a programmer error. + +### Example +``` +let _: (0.0_f32 / 0.0) as u64; +``` +Use instead: +``` +let _: = 0_u64; +``` \ No newline at end of file diff --git a/src/docs/manual_filter.txt b/src/docs/manual_filter.txt new file mode 100644 index 0000000000000..19a4d9319d94b --- /dev/null +++ b/src/docs/manual_filter.txt @@ -0,0 +1,21 @@ +### What it does +Checks for usages of `match` which could be implemented using `filter` + +### Why is this bad? +Using the `filter` method is clearer and more concise. + +### Example +``` +match Some(0) { + Some(x) => if x % 2 == 0 { + Some(x) + } else { + None + }, + None => None, +}; +``` +Use instead: +``` +Some(0).filter(|&x| x % 2 == 0); +``` \ No newline at end of file diff --git a/src/docs/missing_trait_methods.txt b/src/docs/missing_trait_methods.txt new file mode 100644 index 0000000000000..788ad764f8c39 --- /dev/null +++ b/src/docs/missing_trait_methods.txt @@ -0,0 +1,40 @@ +### What it does +Checks if a provided method is used implicitly by a trait +implementation. A usage example would be a wrapper where every method +should perform some operation before delegating to the inner type's +implemenation. + +This lint should typically be enabled on a specific trait `impl` item +rather than globally. + +### Why is this bad? +Indicates that a method is missing. + +### Example +``` +trait Trait { + fn required(); + + fn provided() {} +} + +#[warn(clippy::missing_trait_methods)] +impl Trait for Type { + fn required() { /* ... */ } +} +``` +Use instead: +``` +trait Trait { + fn required(); + + fn provided() {} +} + +#[warn(clippy::missing_trait_methods)] +impl Trait for Type { + fn required() { /* ... */ } + + fn provided() { /* ... */ } +} +``` \ No newline at end of file diff --git a/src/docs/partial_pub_fields.txt b/src/docs/partial_pub_fields.txt new file mode 100644 index 0000000000000..b529adf1547de --- /dev/null +++ b/src/docs/partial_pub_fields.txt @@ -0,0 +1,27 @@ +### What it does +Checks whether partial fields of a struct are public. + +Either make all fields of a type public, or make none of them public + +### Why is this bad? +Most types should either be: +* Abstract data types: complex objects with opaque implementation which guard +interior invariants and expose intentionally limited API to the outside world. +* Data: relatively simple objects which group a bunch of related attributes together. + +### Example +``` +pub struct Color { + pub r: u8, + pub g: u8, + b: u8, +} +``` +Use instead: +``` +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, +} +``` \ No newline at end of file diff --git a/src/docs/unused_format_specs.txt b/src/docs/unused_format_specs.txt new file mode 100644 index 0000000000000..77be3a2fb170d --- /dev/null +++ b/src/docs/unused_format_specs.txt @@ -0,0 +1,24 @@ +### What it does +Detects [formatting parameters] that have no effect on the output of +`format!()`, `println!()` or similar macros. + +### Why is this bad? +Shorter format specifiers are easier to read, it may also indicate that +an expected formatting operation such as adding padding isn't happening. + +### Example +``` +println!("{:.}", 1.0); + +println!("not padded: {:5}", format_args!("...")); +``` +Use instead: +``` +println!("{}", 1.0); + +println!("not padded: {}", format_args!("...")); +// OR +println!("padded: {:5}", format!("...")); +``` + +[formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters \ No newline at end of file diff --git a/tests/compile-test.rs b/tests/compile-test.rs index fa769222d1af3..c10ee969c0146 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -283,7 +283,7 @@ fn run_ui_cargo() { env::set_current_dir(&src_path)?; let cargo_toml_path = case.path().join("Cargo.toml"); - let cargo_content = fs::read(&cargo_toml_path)?; + let cargo_content = fs::read(cargo_toml_path)?; let cargo_parsed: toml::Value = toml::from_str( std::str::from_utf8(&cargo_content).expect("`Cargo.toml` is not a valid utf-8 file!"), ) diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 961525bbd9101..6d0022f7a5ccf 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -20,7 +20,14 @@ fn dogfood_clippy() { } // "" is the root package - for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { + for package in &[ + "", + "clippy_dev", + "clippy_lints", + "clippy_utils", + "lintcheck", + "rustc_tools_util", + ] { run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]); } } diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index a1b8e2ee162cf..07c5941013c1a 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_lints/src/utils/internal_lints/produce_ice.rs:28:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: internal compiler error: unexpected panic diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs index b823ff7fe37f0..9a9790a4bae51 100644 --- a/tests/ui-internal/invalid_paths.rs +++ b/tests/ui-internal/invalid_paths.rs @@ -1,5 +1,5 @@ #![warn(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)] mod paths { // Good path diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed index 4c050332f2cc9..cbbb465230641 100644 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -28,9 +28,9 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { @@ -38,7 +38,7 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = is_type_diagnostic_item(cx, ty, sym::Result); let _ = is_type_diagnostic_item(cx, ty, sym::Result); - #[allow(unused)] + #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = is_type_diagnostic_item(cx, ty, sym::Rc); diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index 6506f1f164ac6..f17fed6c65304 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -28,9 +28,9 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { @@ -38,7 +38,7 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = match_type(cx, ty, RESULT); let _ = match_type(cx, ty, &["core", "result", "Result"]); - #[allow(unused)] + #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = clippy_utils::ty::match_type(cx, ty, rc_path); diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs new file mode 100644 index 0000000000000..b5ff3a5420561 --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs @@ -0,0 +1,16 @@ +#![feature(rustc_private)] +#![allow(unused)] +#![warn(clippy::unnecessary_def_path)] + +extern crate rustc_hir; + +use rustc_hir::LangItem; + +fn main() { + const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + + // Don't lint, not yet a diagnostic or language item + const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; +} diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr new file mode 100644 index 0000000000000..af46d87bf676e --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -0,0 +1,27 @@ +error: hardcoded path to a language item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 + | +LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `LangItem::DerefMut` + = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 + | +LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `sym::deref_method` + +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 + | +LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `sym::Deref` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/as_ptr_cast_mut.rs b/tests/ui/as_ptr_cast_mut.rs new file mode 100644 index 0000000000000..0d1d9258433b2 --- /dev/null +++ b/tests/ui/as_ptr_cast_mut.rs @@ -0,0 +1,37 @@ +#![allow(unused)] +#![warn(clippy::as_ptr_cast_mut)] +#![allow(clippy::wrong_self_convention)] + +struct MutPtrWrapper(Vec); +impl MutPtrWrapper { + fn as_ptr(&mut self) -> *const u8 { + self.0.as_mut_ptr() as *const u8 + } +} + +struct Covariant(*const T); +impl Covariant { + fn as_ptr(self) -> *const T { + self.0 + } +} + +fn main() { + let mut string = String::new(); + let _ = string.as_ptr() as *mut u8; + let _: *mut i8 = string.as_ptr() as *mut _; + let _ = string.as_ptr() as *const i8; + let _ = string.as_mut_ptr(); + let _ = string.as_mut_ptr() as *mut u8; + let _ = string.as_mut_ptr() as *const u8; + + let nn = std::ptr::NonNull::new(4 as *mut u8).unwrap(); + let _ = nn.as_ptr() as *mut u8; + + let mut wrap = MutPtrWrapper(Vec::new()); + let _ = wrap.as_ptr() as *mut u8; + + let mut local = 4; + let ref_with_write_perm = Covariant(std::ptr::addr_of_mut!(local) as *const _); + let _ = ref_with_write_perm.as_ptr() as *mut u8; +} diff --git a/tests/ui/as_ptr_cast_mut.stderr b/tests/ui/as_ptr_cast_mut.stderr new file mode 100644 index 0000000000000..2189c3d2f8556 --- /dev/null +++ b/tests/ui/as_ptr_cast_mut.stderr @@ -0,0 +1,16 @@ +error: casting the result of `as_ptr` to *mut u8 + --> $DIR/as_ptr_cast_mut.rs:21:13 + | +LL | let _ = string.as_ptr() as *mut u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()` + | + = note: `-D clippy::as-ptr-cast-mut` implied by `-D warnings` + +error: casting the result of `as_ptr` to *mut i8 + --> $DIR/as_ptr_cast_mut.rs:22:22 + | +LL | let _: *mut i8 = string.as_ptr() as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/author.stdout b/tests/ui/author.stdout index 597318a556b85..27ad538f24d8c 100644 --- a/tests/ui/author.stdout +++ b/tests/ui/author.stdout @@ -1,14 +1,12 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Cast(expr, cast_ty) = init.kind; - if let TyKind::Path(ref qpath) = cast_ty.kind; - if match_qpath(qpath, &["char"]); - if let ExprKind::Lit(ref lit) = expr.kind; - if let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "x"; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Cast(expr, cast_ty) = init.kind + && let TyKind::Path(ref qpath) = cast_ty.kind + && match_qpath(qpath, &["char"]) + && let ExprKind::Lit(ref lit) = expr.kind + && let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" +{ + // report your lint here } diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index a529981e2e683..9de0550d81d00 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -1,64 +1,58 @@ -if_chain! { - if let ExprKind::Block(block, None) = expr.kind; - if block.stmts.len() == 3; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Lit(ref lit) = init.kind; - if let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "x"; - if let StmtKind::Local(local1) = block.stmts[1].kind; - if let Some(init1) = local1.init; - if let ExprKind::Lit(ref lit1) = init1.kind; - if let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind; - if name1.as_str() == "_t"; - if let StmtKind::Semi(e) = block.stmts[2].kind; - if let ExprKind::Unary(UnOp::Neg, inner) = e.kind; - if let ExprKind::Path(ref qpath) = inner.kind; - if match_qpath(qpath, &["x"]); - if block.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 3 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Lit(ref lit) = init.kind + && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" + && let StmtKind::Local(local1) = block.stmts[1].kind + && let Some(init1) = local1.init + && let ExprKind::Lit(ref lit1) = init1.kind + && let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind + && name1.as_str() == "_t" + && let StmtKind::Semi(e) = block.stmts[2].kind + && let ExprKind::Unary(UnOp::Neg, inner) = e.kind + && let ExprKind::Path(ref qpath) = inner.kind + && match_qpath(qpath, &["x"]) + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::Block(block, None) = expr.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["String", "new"]); - if args.is_empty(); - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "expr"; - if let Some(trailing_expr) = block.expr; - if let ExprKind::Call(func1, args1) = trailing_expr.kind; - if let ExprKind::Path(ref qpath1) = func1.kind; - if match_qpath(qpath1, &["drop"]); - if args1.len() == 1; - if let ExprKind::Path(ref qpath2) = args1[0].kind; - if match_qpath(qpath2, &["expr"]); - then { - // report your lint here - } +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["String", "new"]) + && args.is_empty() + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "expr" + && let Some(trailing_expr) = block.expr + && let ExprKind::Call(func1, args1) = trailing_expr.kind + && let ExprKind::Path(ref qpath1) = func1.kind + && match_qpath(qpath1, &["drop"]) + && args1.len() == 1 + && let ExprKind::Path(ref qpath2) = args1[0].kind + && match_qpath(qpath2, &["expr"]) +{ + // report your lint here } -if_chain! { - if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind; - if let FnRetTy::DefaultReturn(_) = fn_decl.output; - let expr1 = &cx.tcx.hir().body(body_id).value; - if let ExprKind::Call(func, args) = expr1.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _)); - if args.len() == 1; - if let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind; - if let FnRetTy::DefaultReturn(_) = fn_decl1.output; - let expr2 = &cx.tcx.hir().body(body_id1).value; - if let ExprKind::Block(block, None) = expr2.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind + && let FnRetTy::DefaultReturn(_) = fn_decl.output + && expr1 = &cx.tcx.hir().body(body_id).value + && let ExprKind::Call(func, args) = expr1.kind + && let ExprKind::Path(ref qpath) = func.kind + && matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _)) + && args.len() == 1 + && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind + && let FnRetTy::DefaultReturn(_) = fn_decl1.output + && expr2 = &cx.tcx.hir().body(body_id1).value + && let ExprKind::Block(block, None) = expr2.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/call.stdout b/tests/ui/author/call.stdout index 266312d63e50d..f040f6330a64d 100644 --- a/tests/ui/author/call.stdout +++ b/tests/ui/author/call.stdout @@ -1,16 +1,14 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]); - if args.len() == 2; - if let ExprKind::Lit(ref lit) = args[0].kind; - if let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node; - if let ExprKind::Lit(ref lit1) = args[1].kind; - if let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node; - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]) + && args.len() == 2 + && let ExprKind::Lit(ref lit) = args[0].kind + && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node + && let ExprKind::Lit(ref lit1) = args[1].kind + && let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } diff --git a/tests/ui/author/if.stdout b/tests/ui/author/if.stdout index 8d92849b3668f..5d79618820d80 100644 --- a/tests/ui/author/if.stdout +++ b/tests/ui/author/if.stdout @@ -1,50 +1,46 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::If(cond, then, Some(else_expr)) = init.kind; - if let ExprKind::DropTemps(expr) = cond.kind; - if let ExprKind::Lit(ref lit) = expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Binary(op, left, right) = e.kind; - if BinOpKind::Eq == op.node; - if let ExprKind::Lit(ref lit1) = left.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Lit(ref lit2) = right.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node; - if block.expr.is_none(); - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.len() == 1; - if let StmtKind::Semi(e1) = block1.stmts[0].kind; - if let ExprKind::Binary(op1, left1, right1) = e1.kind; - if BinOpKind::Eq == op1.node; - if let ExprKind::Lit(ref lit3) = left1.kind; - if let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node; - if let ExprKind::Lit(ref lit4) = right1.kind; - if let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node; - if block1.expr.is_none(); - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::If(cond, then, Some(else_expr)) = init.kind + && let ExprKind::DropTemps(expr) = cond.kind + && let ExprKind::Lit(ref lit) = expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Block(block, None) = then.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Binary(op, left, right) = e.kind + && BinOpKind::Eq == op.node + && let ExprKind::Lit(ref lit1) = left.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Lit(ref lit2) = right.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node + && block.expr.is_none() + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.len() == 1 + && let StmtKind::Semi(e1) = block1.stmts[0].kind + && let ExprKind::Binary(op1, left1, right1) = e1.kind + && BinOpKind::Eq == op1.node + && let ExprKind::Lit(ref lit3) = left1.kind + && let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node + && let ExprKind::Lit(ref lit4) = right1.kind + && let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node + && block1.expr.is_none() + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } -if_chain! { - if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind; - if let ExprKind::Let(let_expr) = cond.kind; - if let PatKind::Lit(lit_expr) = let_expr.pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Path(ref qpath) = let_expr.init.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.is_empty(); - if block1.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind + && let ExprKind::Let(let_expr) = cond.kind + && let PatKind::Lit(lit_expr) = let_expr.pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Path(ref qpath) = let_expr.init.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = then.kind + && block.stmts.is_empty() + && block.expr.is_none() + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.is_empty() + && block1.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/issue_3849.stdout b/tests/ui/author/issue_3849.stdout index bce4bc702733f..32a3127b85a3e 100644 --- a/tests/ui/author/issue_3849.stdout +++ b/tests/ui/author/issue_3849.stdout @@ -1,14 +1,12 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["std", "mem", "transmute"]); - if args.len() == 1; - if let ExprKind::Path(ref qpath1) = args[0].kind; - if match_qpath(qpath1, &["ZPTR"]); - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["std", "mem", "transmute"]) + && args.len() == 1 + && let ExprKind::Path(ref qpath1) = args[0].kind + && match_qpath(qpath1, &["ZPTR"]) + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } diff --git a/tests/ui/author/loop.stdout b/tests/ui/author/loop.stdout index ceb53fcd49636..94a6436ed5479 100644 --- a/tests/ui/author/loop.stdout +++ b/tests/ui/author/loop.stdout @@ -1,113 +1,101 @@ -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind; - if name.as_str() == "y"; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Path(ref qpath1) = init.kind; - if match_qpath(qpath1, &["y"]); - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind; - if name1.as_str() == "z"; - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind + && name.as_str() == "y" + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Path(ref qpath1) = init.kind + && match_qpath(qpath1, &["y"]) + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind + && name1.as_str() == "z" + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Wild = pat.kind; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Wild = pat.kind + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Wild = pat.kind; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if let Some(label) = destination.label; - if label.ident.as_str() == "'label"; - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Wild = pat.kind + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && let Some(label) = destination.label + && label.ident.as_str() == "'label" + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr); - if let ExprKind::Path(ref qpath) = condition.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr) + && let ExprKind::Path(ref qpath) = condition.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr); - if let PatKind::Lit(lit_expr) = let_pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Path(ref qpath) = let_expr.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = if_then.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) + && let PatKind::Lit(lit_expr) = let_pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Path(ref qpath) = let_expr.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = if_then.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind; - if body.stmts.len() == 1; - if let StmtKind::Semi(e) = body.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if body.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind + && body.stmts.len() == 1 + && let StmtKind::Semi(e) = body.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && body.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/matches.stdout b/tests/ui/author/matches.stdout index 2cf69a035b4c7..88e2ca656a4f6 100644 --- a/tests/ui/author/matches.stdout +++ b/tests/ui/author/matches.stdout @@ -1,38 +1,36 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind; - if let ExprKind::Lit(ref lit) = scrutinee.kind; - if let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node; - if arms.len() == 3; - if let PatKind::Lit(lit_expr) = arms[0].pat.kind; - if let ExprKind::Lit(ref lit1) = lit_expr.kind; - if let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node; - if arms[0].guard.is_none(); - if let ExprKind::Lit(ref lit2) = arms[0].body.kind; - if let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node; - if let PatKind::Lit(lit_expr1) = arms[1].pat.kind; - if let ExprKind::Lit(ref lit3) = lit_expr1.kind; - if let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node; - if arms[1].guard.is_none(); - if let ExprKind::Block(block, None) = arms[1].body.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local1) = block.stmts[0].kind; - if let Some(init1) = local1.init; - if let ExprKind::Lit(ref lit4) = init1.kind; - if let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind; - if name.as_str() == "x"; - if let Some(trailing_expr) = block.expr; - if let ExprKind::Path(ref qpath) = trailing_expr.kind; - if match_qpath(qpath, &["x"]); - if let PatKind::Wild = arms[2].pat.kind; - if arms[2].guard.is_none(); - if let ExprKind::Lit(ref lit5) = arms[2].body.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind; - if name1.as_str() == "a"; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind + && let ExprKind::Lit(ref lit) = scrutinee.kind + && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node + && arms.len() == 3 + && let PatKind::Lit(lit_expr) = arms[0].pat.kind + && let ExprKind::Lit(ref lit1) = lit_expr.kind + && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node + && arms[0].guard.is_none() + && let ExprKind::Lit(ref lit2) = arms[0].body.kind + && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node + && let PatKind::Lit(lit_expr1) = arms[1].pat.kind + && let ExprKind::Lit(ref lit3) = lit_expr1.kind + && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node + && arms[1].guard.is_none() + && let ExprKind::Block(block, None) = arms[1].body.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local1) = block.stmts[0].kind + && let Some(init1) = local1.init + && let ExprKind::Lit(ref lit4) = init1.kind + && let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind + && name.as_str() == "x" + && let Some(trailing_expr) = block.expr + && let ExprKind::Path(ref qpath) = trailing_expr.kind + && match_qpath(qpath, &["x"]) + && let PatKind::Wild = arms[2].pat.kind + && arms[2].guard.is_none() + && let ExprKind::Lit(ref lit5) = arms[2].body.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind + && name1.as_str() == "a" +{ + // report your lint here } diff --git a/tests/ui/author/repeat.stdout b/tests/ui/author/repeat.stdout index 471bbce4f4185..c2a369610cc1b 100644 --- a/tests/ui/author/repeat.stdout +++ b/tests/ui/author/repeat.stdout @@ -1,12 +1,10 @@ -if_chain! { - if let ExprKind::Repeat(value, length) = expr.kind; - if let ExprKind::Lit(ref lit) = value.kind; - if let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node; - if let ArrayLen::Body(anon_const) = length; - let expr1 = &cx.tcx.hir().body(anon_const.body).value; - if let ExprKind::Lit(ref lit1) = expr1.kind; - if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node; - then { - // report your lint here - } +if let ExprKind::Repeat(value, length) = expr.kind + && let ExprKind::Lit(ref lit) = value.kind + && let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node + && let ArrayLen::Body(anon_const) = length + && expr1 = &cx.tcx.hir().body(anon_const.body).value + && let ExprKind::Lit(ref lit1) = expr1.kind + && let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node +{ + // report your lint here } diff --git a/tests/ui/author/struct.stdout b/tests/ui/author/struct.stdout index b5bbc9e213c6e..0b332d5e7d0e1 100644 --- a/tests/ui/author/struct.stdout +++ b/tests/ui/author/struct.stdout @@ -1,64 +1,56 @@ -if_chain! { - if let ExprKind::Struct(qpath, fields, None) = expr.kind; - if match_qpath(qpath, &["Test"]); - if fields.len() == 1; - if fields[0].ident.as_str() == "field"; - if let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind; - if let ExprKind::DropTemps(expr1) = cond.kind; - if let ExprKind::Lit(ref lit) = expr1.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.is_empty(); - if let Some(trailing_expr) = block.expr; - if let ExprKind::Lit(ref lit1) = trailing_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.is_empty(); - if let Some(trailing_expr1) = block1.expr; - if let ExprKind::Lit(ref lit2) = trailing_expr1.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node; - then { - // report your lint here - } +if let ExprKind::Struct(qpath, fields, None) = expr.kind + && match_qpath(qpath, &["Test"]) + && fields.len() == 1 + && fields[0].ident.as_str() == "field" + && let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind + && let ExprKind::DropTemps(expr1) = cond.kind + && let ExprKind::Lit(ref lit) = expr1.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Block(block, None) = then.kind + && block.stmts.is_empty() + && let Some(trailing_expr) = block.expr + && let ExprKind::Lit(ref lit1) = trailing_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.is_empty() + && let Some(trailing_expr1) = block1.expr + && let ExprKind::Lit(ref lit2) = trailing_expr1.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node +{ + // report your lint here } -if_chain! { - if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind; - if match_qpath(qpath, &["Test"]); - if fields.len() == 1; - if fields[0].ident.as_str() == "field"; - if let PatKind::Lit(lit_expr) = fields[0].pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; - if arm.guard.is_none(); - if let ExprKind::Block(block, None) = arm.body.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind + && match_qpath(qpath, &["Test"]) + && fields.len() == 1 + && fields[0].ident.as_str() == "field" + && let PatKind::Lit(lit_expr) = fields[0].pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node + && arm.guard.is_none() + && let ExprKind::Block(block, None) = arm.body.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind; - if match_qpath(qpath, &["TestTuple"]); - if fields.len() == 1; - if let PatKind::Lit(lit_expr) = fields[0].kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; - if arm.guard.is_none(); - if let ExprKind::Block(block, None) = arm.body.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind + && match_qpath(qpath, &["TestTuple"]) + && fields.len() == 1 + && let PatKind::Lit(lit_expr) = fields[0].kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node + && arm.guard.is_none() + && let ExprKind::Block(block, None) = arm.body.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind; - if method_name.ident.as_str() == "test"; - if let ExprKind::Path(ref qpath) = receiver.kind; - if match_qpath(qpath, &["test_method_call"]); - if args.is_empty(); - then { - // report your lint here - } +if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind + && method_name.ident.as_str() == "test" + && let ExprKind::Path(ref qpath) = receiver.kind + && match_qpath(qpath, &["test_method_call"]) + && args.is_empty() +{ + // report your lint here } diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed new file mode 100644 index 0000000000000..911fa856aa0a3 --- /dev/null +++ b/tests/ui/box_default.fixed @@ -0,0 +1,57 @@ +// run-rustfix +#![warn(clippy::box_default)] + +#[derive(Default)] +struct ImplementsDefault; + +struct OwnDefault; + +impl OwnDefault { + fn default() -> Self { + Self + } +} + +macro_rules! outer { + ($e: expr) => { + $e + }; +} + +fn main() { + let _string: Box = Box::default(); + let _byte = Box::::default(); + let _vec = Box::>::default(); + let _impl = Box::::default(); + let _impl2 = Box::::default(); + let _impl3: Box = Box::default(); + let _own = Box::new(OwnDefault::default()); // should not lint + let _in_macro = outer!(Box::::default()); + let _string_default = outer!(Box::::default()); + let _vec2: Box> = Box::default(); + let _vec3: Box> = Box::default(); + let _vec4: Box<_> = Box::>::default(); + let _more = ret_ty_fn(); + call_ty_fn(Box::default()); +} + +fn ret_ty_fn() -> Box { + Box::::default() +} + +#[allow(clippy::boxed_local)] +fn call_ty_fn(_b: Box) { + issue_9621_dyn_trait(); +} + +use std::io::{Read, Result}; + +impl Read for ImplementsDefault { + fn read(&mut self, _: &mut [u8]) -> Result { + Ok(0) + } +} + +fn issue_9621_dyn_trait() { + let _: Box = Box::::default(); +} diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index dc522705bc624..20019c2ee5a07 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::box_default)] #[derive(Default)] @@ -26,6 +27,31 @@ fn main() { let _impl3: Box = Box::new(Default::default()); let _own = Box::new(OwnDefault::default()); // should not lint let _in_macro = outer!(Box::new(String::new())); - // false negative: default is from different expansion + let _string_default = outer!(Box::new(String::from(""))); let _vec2: Box> = Box::new(vec![]); + let _vec3: Box> = Box::new(Vec::from([])); + let _vec4: Box<_> = Box::new(Vec::from([false; 0])); + let _more = ret_ty_fn(); + call_ty_fn(Box::new(u8::default())); +} + +fn ret_ty_fn() -> Box { + Box::new(bool::default()) +} + +#[allow(clippy::boxed_local)] +fn call_ty_fn(_b: Box) { + issue_9621_dyn_trait(); +} + +use std::io::{Read, Result}; + +impl Read for ImplementsDefault { + fn read(&mut self, _: &mut [u8]) -> Result { + Ok(0) + } +} + +fn issue_9621_dyn_trait() { + let _: Box = Box::new(ImplementsDefault::default()); } diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr index b2030e95acb10..5ea410331afb3 100644 --- a/tests/ui/box_default.stderr +++ b/tests/ui/box_default.stderr @@ -1,59 +1,88 @@ error: `Box::new(_)` of default value - --> $DIR/box_default.rs:21:32 + --> $DIR/box_default.rs:22:32 | LL | let _string: Box = Box::new(Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` | - = help: use `Box::default()` instead = note: `-D clippy::box-default` implied by `-D warnings` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:22:17 + --> $DIR/box_default.rs:23:17 | LL | let _byte = Box::new(u8::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:23:16 + --> $DIR/box_default.rs:24:16 | LL | let _vec = Box::new(Vec::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:24:17 + --> $DIR/box_default.rs:25:17 | LL | let _impl = Box::new(ImplementsDefault::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:25:18 + --> $DIR/box_default.rs:26:18 | LL | let _impl2 = Box::new(::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:26:42 + --> $DIR/box_default.rs:27:42 | LL | let _impl3: Box = Box::new(Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:28:28 + --> $DIR/box_default.rs:29:28 | LL | let _in_macro = outer!(Box::new(String::new())); - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:30:34 + | +LL | let _string_default = outer!(Box::new(String::from(""))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:31:46 + | +LL | let _vec2: Box> = Box::new(vec![]); + | ^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:32:33 + | +LL | let _vec3: Box> = Box::new(Vec::from([])); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:33:25 + | +LL | let _vec4: Box<_> = Box::new(Vec::from([false; 0])); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:35:16 + | +LL | call_ty_fn(Box::new(u8::default())); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:39:5 + | +LL | Box::new(bool::default()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:56:28 | - = help: use `Box::default()` instead +LL | let _: Box = Box::new(ImplementsDefault::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` -error: aborting due to 7 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/box_default_no_std.rs b/tests/ui/box_default_no_std.rs new file mode 100644 index 0000000000000..4326abc9a5410 --- /dev/null +++ b/tests/ui/box_default_no_std.rs @@ -0,0 +1,33 @@ +#![feature(lang_items, start, libc)] +#![warn(clippy::box_default)] +#![no_std] + +pub struct NotBox { + _value: T, +} + +impl NotBox { + pub fn new(value: T) -> Self { + Self { _value: value } + } +} + +impl Default for NotBox { + fn default() -> Self { + Self::new(T::default()) + } +} + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let _p = NotBox::new(isize::default()); + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index a37f3fec20f1e..e6bf944c7a5ec 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,6 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] fn main() { let x: i32 = -42; @@ -30,3 +32,17 @@ fn main() { let _ = (x as i64 - y as i64).unsigned_abs() as u32; } + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + +fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + assert_eq!(10u32, x.unsigned_abs()); +} diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 5706930af5a05..c87320b5209db 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,6 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] fn main() { let x: i32 = -42; @@ -30,3 +32,17 @@ fn main() { let _ = (x as i64 - y as i64).abs() as u32; } + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + +fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index 7cea11c183d23..1b39c554b0384 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -1,5 +1,5 @@ error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:7:18 + --> $DIR/cast_abs_to_unsigned.rs:9:18 | LL | let y: u32 = x.abs() as u32; | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` @@ -7,100 +7,106 @@ LL | let y: u32 = x.abs() as u32; = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:11:20 + --> $DIR/cast_abs_to_unsigned.rs:13:20 | LL | let _: usize = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:12:20 + --> $DIR/cast_abs_to_unsigned.rs:14:20 | LL | let _: usize = a.abs() as _; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:13:13 + --> $DIR/cast_abs_to_unsigned.rs:15:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:16:13 + --> $DIR/cast_abs_to_unsigned.rs:18:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:17:13 + --> $DIR/cast_abs_to_unsigned.rs:19:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:18:13 + --> $DIR/cast_abs_to_unsigned.rs:20:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:19:13 + --> $DIR/cast_abs_to_unsigned.rs:21:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:20:13 + --> $DIR/cast_abs_to_unsigned.rs:22:13 | LL | let _ = a.abs() as u64; | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:21:13 + --> $DIR/cast_abs_to_unsigned.rs:23:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:24:13 + --> $DIR/cast_abs_to_unsigned.rs:26:13 | LL | let _ = a.abs() as usize; | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:25:13 + --> $DIR/cast_abs_to_unsigned.rs:27:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:26:13 + --> $DIR/cast_abs_to_unsigned.rs:28:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:27:13 + --> $DIR/cast_abs_to_unsigned.rs:29:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:28:13 + --> $DIR/cast_abs_to_unsigned.rs:30:13 | LL | let _ = a.abs() as u64; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:29:13 + --> $DIR/cast_abs_to_unsigned.rs:31:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:31:13 + --> $DIR/cast_abs_to_unsigned.rs:33:13 | LL | let _ = (x as i64 - y as i64).abs() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()` -error: aborting due to 17 previous errors +error: casting the result of `i32::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:47:23 + | +LL | assert_eq!(10u32, x.abs() as u32); + | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` + +error: aborting due to 18 previous errors diff --git a/tests/ui/cast_lossless_bool.fixed b/tests/ui/cast_lossless_bool.fixed index 9e2da45c37858..af13b755e310a 100644 --- a/tests/ui/cast_lossless_bool.fixed +++ b/tests/ui/cast_lossless_bool.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -40,3 +41,15 @@ mod cast_lossless_in_impl { } } } + +fn msrv_1_27() { + #![clippy::msrv = "1.27"] + + let _ = true as u8; +} + +fn msrv_1_28() { + #![clippy::msrv = "1.28"] + + let _ = u8::from(true); +} diff --git a/tests/ui/cast_lossless_bool.rs b/tests/ui/cast_lossless_bool.rs index b6f6c59a01f95..3b06af899c60d 100644 --- a/tests/ui/cast_lossless_bool.rs +++ b/tests/ui/cast_lossless_bool.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -40,3 +41,15 @@ mod cast_lossless_in_impl { } } } + +fn msrv_1_27() { + #![clippy::msrv = "1.27"] + + let _ = true as u8; +} + +fn msrv_1_28() { + #![clippy::msrv = "1.28"] + + let _ = true as u8; +} diff --git a/tests/ui/cast_lossless_bool.stderr b/tests/ui/cast_lossless_bool.stderr index 6b148336011d5..768b033d10a28 100644 --- a/tests/ui/cast_lossless_bool.stderr +++ b/tests/ui/cast_lossless_bool.stderr @@ -1,5 +1,5 @@ error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> $DIR/cast_lossless_bool.rs:8:13 + --> $DIR/cast_lossless_bool.rs:9:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` @@ -7,76 +7,82 @@ LL | let _ = true as u8; = note: `-D clippy::cast-lossless` implied by `-D warnings` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:9:13 + --> $DIR/cast_lossless_bool.rs:10:13 | LL | let _ = true as u16; | ^^^^^^^^^^^ help: try: `u16::from(true)` error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)` - --> $DIR/cast_lossless_bool.rs:10:13 + --> $DIR/cast_lossless_bool.rs:11:13 | LL | let _ = true as u32; | ^^^^^^^^^^^ help: try: `u32::from(true)` error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)` - --> $DIR/cast_lossless_bool.rs:11:13 + --> $DIR/cast_lossless_bool.rs:12:13 | LL | let _ = true as u64; | ^^^^^^^^^^^ help: try: `u64::from(true)` error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)` - --> $DIR/cast_lossless_bool.rs:12:13 + --> $DIR/cast_lossless_bool.rs:13:13 | LL | let _ = true as u128; | ^^^^^^^^^^^^ help: try: `u128::from(true)` error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)` - --> $DIR/cast_lossless_bool.rs:13:13 + --> $DIR/cast_lossless_bool.rs:14:13 | LL | let _ = true as usize; | ^^^^^^^^^^^^^ help: try: `usize::from(true)` error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)` - --> $DIR/cast_lossless_bool.rs:15:13 + --> $DIR/cast_lossless_bool.rs:16:13 | LL | let _ = true as i8; | ^^^^^^^^^^ help: try: `i8::from(true)` error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)` - --> $DIR/cast_lossless_bool.rs:16:13 + --> $DIR/cast_lossless_bool.rs:17:13 | LL | let _ = true as i16; | ^^^^^^^^^^^ help: try: `i16::from(true)` error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)` - --> $DIR/cast_lossless_bool.rs:17:13 + --> $DIR/cast_lossless_bool.rs:18:13 | LL | let _ = true as i32; | ^^^^^^^^^^^ help: try: `i32::from(true)` error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)` - --> $DIR/cast_lossless_bool.rs:18:13 + --> $DIR/cast_lossless_bool.rs:19:13 | LL | let _ = true as i64; | ^^^^^^^^^^^ help: try: `i64::from(true)` error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)` - --> $DIR/cast_lossless_bool.rs:19:13 + --> $DIR/cast_lossless_bool.rs:20:13 | LL | let _ = true as i128; | ^^^^^^^^^^^^ help: try: `i128::from(true)` error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)` - --> $DIR/cast_lossless_bool.rs:20:13 + --> $DIR/cast_lossless_bool.rs:21:13 | LL | let _ = true as isize; | ^^^^^^^^^^^^^ help: try: `isize::from(true)` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:23:13 + --> $DIR/cast_lossless_bool.rs:24:13 | LL | let _ = (true | false) as u16; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)` -error: aborting due to 13 previous errors +error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` + --> $DIR/cast_lossless_bool.rs:54:13 + | +LL | let _ = true as u8; + | ^^^^^^^^^^ help: try: `u8::from(true)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/cast_nan_to_int.rs b/tests/ui/cast_nan_to_int.rs new file mode 100644 index 0000000000000..287c5aa216bd3 --- /dev/null +++ b/tests/ui/cast_nan_to_int.rs @@ -0,0 +1,18 @@ +#![warn(clippy::cast_nan_to_int)] +#![allow(clippy::eq_op)] + +fn main() { + let _ = (0.0_f32 / -0.0) as usize; + let _ = (f64::INFINITY * -0.0) as usize; + let _ = (0.0 * f32::INFINITY) as usize; + + let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize; + let _ = (f32::INFINITY - f32::INFINITY) as usize; + let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize; + + // those won't be linted: + let _ = (1.0_f32 / 0.0) as usize; + let _ = (f32::INFINITY * f32::NEG_INFINITY) as usize; + let _ = (f32::INFINITY - f32::NEG_INFINITY) as usize; + let _ = (f64::INFINITY - 0.0) as usize; +} diff --git a/tests/ui/cast_nan_to_int.stderr b/tests/ui/cast_nan_to_int.stderr new file mode 100644 index 0000000000000..3539be75a19db --- /dev/null +++ b/tests/ui/cast_nan_to_int.stderr @@ -0,0 +1,51 @@ +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:5:13 + | +LL | let _ = (0.0_f32 / -0.0) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + = note: `-D clippy::cast-nan-to-int` implied by `-D warnings` + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:6:13 + | +LL | let _ = (f64::INFINITY * -0.0) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:7:13 + | +LL | let _ = (0.0 * f32::INFINITY) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:9:13 + | +LL | let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:10:13 + | +LL | let _ = (f32::INFINITY - f32::INFINITY) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:11:13 + | +LL | let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: aborting due to 6 previous errors + diff --git a/tests/ui/cfg_attr_rustfmt.fixed b/tests/ui/cfg_attr_rustfmt.fixed index 061a4ab9b2ef8..8a5645b22ed19 100644 --- a/tests/ui/cfg_attr_rustfmt.fixed +++ b/tests/ui/cfg_attr_rustfmt.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, custom_inner_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -29,3 +29,17 @@ mod foo { pub fn f() {} } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+29; +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + #[rustfmt::skip] + 1+30; +} diff --git a/tests/ui/cfg_attr_rustfmt.rs b/tests/ui/cfg_attr_rustfmt.rs index 035169fab85be..2fb140efae768 100644 --- a/tests/ui/cfg_attr_rustfmt.rs +++ b/tests/ui/cfg_attr_rustfmt.rs @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, custom_inner_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -29,3 +29,17 @@ mod foo { pub fn f() {} } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+29; +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+30; +} diff --git a/tests/ui/cfg_attr_rustfmt.stderr b/tests/ui/cfg_attr_rustfmt.stderr index c1efd47db90b0..08df7b2b39a0c 100644 --- a/tests/ui/cfg_attr_rustfmt.stderr +++ b/tests/ui/cfg_attr_rustfmt.stderr @@ -12,5 +12,11 @@ error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes LL | #[cfg_attr(rustfmt, rustfmt_skip)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` -error: aborting due to 2 previous errors +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes + --> $DIR/cfg_attr_rustfmt.rs:43:5 + | +LL | #[cfg_attr(rustfmt, rustfmt::skip)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` + +error: aborting due to 3 previous errors diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index cb7100bc9efae..f936957cb40c6 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -1,7 +1,9 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, + unused, // Int::max_value will be deprecated in the future deprecated, )] @@ -76,4 +78,18 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +fn msrv_1_33() { + #![clippy::msrv = "1.33"] + + let value: i64 = 33; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let value: i64 = 34; + let _ = u32::try_from(value).is_ok(); +} + fn main() {} diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index ed4e0692388a5..77aec713ff316 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -1,7 +1,9 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, + unused, // Int::max_value will be deprecated in the future deprecated, )] @@ -76,4 +78,18 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +fn msrv_1_33() { + #![clippy::msrv = "1.33"] + + let value: i64 = 33; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let value: i64 = 34; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + fn main() {} diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index 2e518040561c4..b2bf7af8daf87 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> $DIR/checked_conversions.rs:15:13 + --> $DIR/checked_conversions.rs:17:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -7,94 +7,100 @@ 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 - --> $DIR/checked_conversions.rs:16:13 + --> $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 - --> $DIR/checked_conversions.rs:20:13 + --> $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 - --> $DIR/checked_conversions.rs:21:13 + --> $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 - --> $DIR/checked_conversions.rs:25:13 + --> $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 - --> $DIR/checked_conversions.rs:26:13 + --> $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 - --> $DIR/checked_conversions.rs:32:13 + --> $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 - --> $DIR/checked_conversions.rs:33:13 + --> $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 - --> $DIR/checked_conversions.rs:37:13 + --> $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:38:13 + --> $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:44:13 + --> $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:45:13 + --> $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:49:13 + --> $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:50:13 + --> $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:54:13 + --> $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:55:13 + --> $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 +error: checked cast can be simplified + --> $DIR/checked_conversions.rs:92:13 + | +LL | let _ = value <= (u32::MAX as i64) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` + +error: aborting due to 17 previous errors diff --git a/tests/ui/cloned_instead_of_copied.fixed b/tests/ui/cloned_instead_of_copied.fixed index 4eb999e18e64e..42ed232d1001c 100644 --- a/tests/ui/cloned_instead_of_copied.fixed +++ b/tests/ui/cloned_instead_of_copied.fixed @@ -1,5 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] +#![allow(unused)] fn main() { // yay @@ -13,3 +16,24 @@ fn main() { let _ = [String::new()].iter().cloned(); let _ = Some(&String::new()).cloned(); } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).copied(); // Option::copied needs 1.35 +} + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + let _ = [1].iter().copied(); // Iterator::copied needs 1.36 + let _ = Some(&1).copied(); +} diff --git a/tests/ui/cloned_instead_of_copied.rs b/tests/ui/cloned_instead_of_copied.rs index 894496c0ebbb5..471bd9654cc13 100644 --- a/tests/ui/cloned_instead_of_copied.rs +++ b/tests/ui/cloned_instead_of_copied.rs @@ -1,5 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] +#![allow(unused)] fn main() { // yay @@ -13,3 +16,24 @@ fn main() { let _ = [String::new()].iter().cloned(); let _ = Some(&String::new()).cloned(); } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); // Option::copied needs 1.35 +} + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 + let _ = Some(&1).cloned(); +} diff --git a/tests/ui/cloned_instead_of_copied.stderr b/tests/ui/cloned_instead_of_copied.stderr index e0707d3214689..914c9a91e8300 100644 --- a/tests/ui/cloned_instead_of_copied.stderr +++ b/tests/ui/cloned_instead_of_copied.stderr @@ -1,5 +1,5 @@ error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:6:24 + --> $DIR/cloned_instead_of_copied.rs:9:24 | LL | let _ = [1].iter().cloned(); | ^^^^^^ help: try: `copied` @@ -7,28 +7,46 @@ LL | let _ = [1].iter().cloned(); = 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 + --> $DIR/cloned_instead_of_copied.rs:10: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 + --> $DIR/cloned_instead_of_copied.rs:11: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 + --> $DIR/cloned_instead_of_copied.rs:12: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 + --> $DIR/cloned_instead_of_copied.rs:13:32 | LL | let _ = Box::new(Some(&1)).cloned(); | ^^^^^^ help: try: `copied` -error: aborting due to 5 previous errors +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:31:22 + | +LL | let _ = Some(&1).cloned(); // Option::copied needs 1.35 + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:37:24 + | +LL | let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:38:22 + | +LL | let _ = Some(&1).cloned(); + | ^^^^^^ help: try: `copied` + +error: aborting due to 8 previous errors diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 7d53e08345d30..1d7a72846419f 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -253,6 +253,27 @@ fn negative_cases(res_opt: Result, String>, res_res: Result>, b: () }, + B, +} + +pub fn test_1(x: Issue9647) { + if let Issue9647::A { a, .. } = x { + if let Some(u) = a { + println!("{u:?}") + } + } +} + +pub fn test_2(x: Issue9647) { + if let Issue9647::A { a: Some(a), .. } = x { + if let Some(u) = a { + println!("{u}") + } + } +} + fn make() -> T { unimplemented!() } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 2580bef58091e..0294be60b43fd 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -175,5 +175,37 @@ LL | Some(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: aborting due to 10 previous errors +error: this `if let` can be collapsed into the outer `if let` + --> $DIR/collapsible_match.rs:263:9 + | +LL | / if let Some(u) = a { +LL | | println!("{u:?}") +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> $DIR/collapsible_match.rs:262:27 + | +LL | if let Issue9647::A { a, .. } = x { + | ^ replace this binding +LL | if let Some(u) = a { + | ^^^^^^^ with this pattern, prefixed by a: + +error: this `if let` can be collapsed into the outer `if let` + --> $DIR/collapsible_match.rs:271:9 + | +LL | / if let Some(u) = a { +LL | | println!("{u}") +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> $DIR/collapsible_match.rs:270:35 + | +LL | if let Issue9647::A { a: Some(a), .. } = x { + | ^ replace this binding +LL | if let Some(u) = a { + | ^^^^^^^ with this pattern + +error: aborting due to 12 previous errors diff --git a/tests/ui/crashes/ice-9625.rs b/tests/ui/crashes/ice-9625.rs new file mode 100644 index 0000000000000..a765882b5d818 --- /dev/null +++ b/tests/ui/crashes/ice-9625.rs @@ -0,0 +1,4 @@ +fn main() { + let x = &1; + let _ = &1 < x && x < &10; +} diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed index a28bff76755b6..a370ccc76962e 100644 --- a/tests/ui/default_numeric_fallback_f64.fixed +++ b/tests/ui/default_numeric_fallback_f64.fixed @@ -33,6 +33,7 @@ mod basic_expr { let x: [f64; 3] = [1., 2., 3.]; let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) }; let x: _ = 1.; + const X: f32 = 1.; } } @@ -59,6 +60,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2. }; + + const X: f32 = { + // Should lint this because this literal is not bound to any types. + let y = 1.0_f64; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1. + }; } } diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs index b48435cc7b282..2476fe95141de 100644 --- a/tests/ui/default_numeric_fallback_f64.rs +++ b/tests/ui/default_numeric_fallback_f64.rs @@ -33,6 +33,7 @@ mod basic_expr { let x: [f64; 3] = [1., 2., 3.]; let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) }; let x: _ = 1.; + const X: f32 = 1.; } } @@ -59,6 +60,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2. }; + + const X: f32 = { + // 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. + }; } } diff --git a/tests/ui/default_numeric_fallback_f64.stderr b/tests/ui/default_numeric_fallback_f64.stderr index f8b6c7746edbb..5df2f642388dc 100644 --- a/tests/ui/default_numeric_fallback_f64.stderr +++ b/tests/ui/default_numeric_fallback_f64.stderr @@ -61,79 +61,85 @@ LL | _ => 1., | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:43:21 + --> $DIR/default_numeric_fallback_f64.rs:44:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:51:21 + --> $DIR/default_numeric_fallback_f64.rs:52:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:57:21 + --> $DIR/default_numeric_fallback_f64.rs:58:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:69:9 + --> $DIR/default_numeric_fallback_f64.rs:66:21 + | +LL | let y = 1.; + | ^^ help: consider adding suffix: `1.0_f64` + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_f64.rs:78:9 | LL | 1. | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:75:27 + --> $DIR/default_numeric_fallback_f64.rs:84:27 | LL | let f = || -> _ { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:79:29 + --> $DIR/default_numeric_fallback_f64.rs:88:29 | LL | let f = || -> f64 { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:93:21 + --> $DIR/default_numeric_fallback_f64.rs:102:21 | LL | generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:96:32 + --> $DIR/default_numeric_fallback_f64.rs:105:32 | LL | let x: _ = generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:114:28 + --> $DIR/default_numeric_fallback_f64.rs:123:28 | LL | GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:117:36 + --> $DIR/default_numeric_fallback_f64.rs:126:36 | LL | let _ = GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:135:24 + --> $DIR/default_numeric_fallback_f64.rs:144:24 | LL | GenericEnum::X(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:155:23 + --> $DIR/default_numeric_fallback_f64.rs:164:23 | LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:162:21 + --> $DIR/default_numeric_fallback_f64.rs:171:21 | LL | let x = 22.; | ^^^ help: consider adding suffix: `22.0_f64` @@ -143,5 +149,5 @@ LL | internal_macro!(); | = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 23 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index 55451cf2f7d0f..3f4994f0453b1 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -33,6 +33,8 @@ mod basic_expr { let x: [i32; 3] = [1, 2, 3]; let x: (i32, i32) = if true { (1, 2) } else { (3, 4) }; let x: _ = 1; + let x: u64 = 1; + const CONST_X: i8 = 1; } } @@ -59,6 +61,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2 }; + + const CONST_X: i32 = { + // Should lint this because this literal is not bound to any types. + let y = 1_i32; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + }; } } diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index 62d72f2febaaa..2df0e09787f90 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -33,6 +33,8 @@ mod basic_expr { let x: [i32; 3] = [1, 2, 3]; let x: (i32, i32) = if true { (1, 2) } else { (3, 4) }; let x: _ = 1; + let x: u64 = 1; + const CONST_X: i8 = 1; } } @@ -59,6 +61,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2 }; + + const CONST_X: i32 = { + // 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 + }; } } diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr index f7c5e724c403c..6f219c3fc2b0e 100644 --- a/tests/ui/default_numeric_fallback_i32.stderr +++ b/tests/ui/default_numeric_fallback_i32.stderr @@ -73,79 +73,85 @@ LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:43:21 + --> $DIR/default_numeric_fallback_i32.rs:45:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:51:21 + --> $DIR/default_numeric_fallback_i32.rs:53:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:57:21 + --> $DIR/default_numeric_fallback_i32.rs:59:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:69:9 + --> $DIR/default_numeric_fallback_i32.rs:67:21 + | +LL | let y = 1; + | ^ help: consider adding suffix: `1_i32` + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_i32.rs:79:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:75:27 + --> $DIR/default_numeric_fallback_i32.rs:85:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:79:29 + --> $DIR/default_numeric_fallback_i32.rs:89:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:93:21 + --> $DIR/default_numeric_fallback_i32.rs:103:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:96:32 + --> $DIR/default_numeric_fallback_i32.rs:106:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:114:28 + --> $DIR/default_numeric_fallback_i32.rs:124:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:117:36 + --> $DIR/default_numeric_fallback_i32.rs:127:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:135:24 + --> $DIR/default_numeric_fallback_i32.rs:145:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:155:23 + --> $DIR/default_numeric_fallback_i32.rs:165:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:162:21 + --> $DIR/default_numeric_fallback_i32.rs:172:21 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -155,5 +161,5 @@ LL | internal_macro!(); | = 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 +error: aborting due to 26 previous errors diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed index 7e18d70bae400..3bac738acd65b 100644 --- a/tests/ui/err_expect.fixed +++ b/tests/ui/err_expect.fixed @@ -1,5 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] +#![allow(unused)] + struct MyTypeNonDebug; #[derive(Debug)] @@ -12,3 +15,17 @@ fn main() { let test_non_debug: Result = Ok(MyTypeNonDebug); test_non_debug.err().expect("Testing non debug type"); } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let x: Result = Ok(16); + x.err().expect("16"); +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let x: Result = Ok(17); + x.expect_err("17"); +} diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs index bf8c3c9fb8c98..6e7c47d9ad3cf 100644 --- a/tests/ui/err_expect.rs +++ b/tests/ui/err_expect.rs @@ -1,5 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] +#![allow(unused)] + struct MyTypeNonDebug; #[derive(Debug)] @@ -12,3 +15,17 @@ fn main() { let test_non_debug: Result = Ok(MyTypeNonDebug); test_non_debug.err().expect("Testing non debug type"); } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let x: Result = Ok(16); + x.err().expect("16"); +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let x: Result = Ok(17); + x.err().expect("17"); +} diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr index ffd97e00a5c09..91a6cf8de65fc 100644 --- a/tests/ui/err_expect.stderr +++ b/tests/ui/err_expect.stderr @@ -1,10 +1,16 @@ error: called `.err().expect()` on a `Result` value - --> $DIR/err_expect.rs:10:16 + --> $DIR/err_expect.rs:13:16 | LL | test_debug.err().expect("Testing debug type"); | ^^^^^^^^^^^^ help: try: `expect_err` | = note: `-D clippy::err-expect` implied by `-D warnings` -error: aborting due to previous error +error: called `.err().expect()` on a `Result` value + --> $DIR/err_expect.rs:30:7 + | +LL | x.err().expect("17"); + | ^^^^^^^^^^^^ help: try: `expect_err` + +error: aborting due to 2 previous errors diff --git a/tests/ui/filter_map_next_fixable.fixed b/tests/ui/filter_map_next_fixable.fixed index c3992d7e92cf3..41828ddd7acde 100644 --- a/tests/ui/filter_map_next_fixable.fixed +++ b/tests/ui/filter_map_next_fixable.fixed @@ -1,6 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] +#![allow(unused)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; @@ -8,3 +10,17 @@ fn main() { let element: Option = a.iter().find_map(|s| s.parse().ok()); assert_eq!(element, Some(1)); } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().find_map(|s| s.parse().ok()); +} diff --git a/tests/ui/filter_map_next_fixable.rs b/tests/ui/filter_map_next_fixable.rs index 447219a968391..be492a81b45ec 100644 --- a/tests/ui/filter_map_next_fixable.rs +++ b/tests/ui/filter_map_next_fixable.rs @@ -1,6 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] +#![allow(unused)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; @@ -8,3 +10,17 @@ fn main() { let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); assert_eq!(element, Some(1)); } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr index 3bb062ffd7a32..e789efeabd550 100644 --- a/tests/ui/filter_map_next_fixable.stderr +++ b/tests/ui/filter_map_next_fixable.stderr @@ -1,10 +1,16 @@ 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 + --> $DIR/filter_map_next_fixable.rs:10: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 +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead + --> $DIR/filter_map_next_fixable.rs:25:26 + | +LL | let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` + +error: aborting due to 2 previous errors diff --git a/tests/ui/format_args.fixed b/tests/ui/format_args.fixed index 24cf0847dd588..825e122be5a5f 100644 --- a/tests/ui/format_args.fixed +++ b/tests/ui/format_args.fixed @@ -3,6 +3,7 @@ #![allow(unused)] #![allow( clippy::assertions_on_constants, + clippy::double_parens, clippy::eq_op, clippy::print_literal, clippy::uninlined_format_args @@ -114,6 +115,8 @@ fn main() { println!("error: something failed at {}", my_other_macro!()); // https://github.com/rust-lang/rust-clippy/issues/7903 println!("{foo}{foo:?}", foo = "foo".to_string()); + print!("{}", (Location::caller())); + print!("{}", ((Location::caller()))); } fn issue8643(vendor_id: usize, product_id: usize, name: &str) { diff --git a/tests/ui/format_args.rs b/tests/ui/format_args.rs index 753babf0afdc7..a41e53389e52a 100644 --- a/tests/ui/format_args.rs +++ b/tests/ui/format_args.rs @@ -3,6 +3,7 @@ #![allow(unused)] #![allow( clippy::assertions_on_constants, + clippy::double_parens, clippy::eq_op, clippy::print_literal, clippy::uninlined_format_args @@ -114,6 +115,8 @@ fn main() { println!("error: something failed at {}", my_other_macro!()); // https://github.com/rust-lang/rust-clippy/issues/7903 println!("{foo}{foo:?}", foo = "foo".to_string()); + print!("{}", (Location::caller().to_string())); + print!("{}", ((Location::caller()).to_string())); } fn issue8643(vendor_id: usize, product_id: usize, name: &str) { diff --git a/tests/ui/format_args.stderr b/tests/ui/format_args.stderr index 68b0bb9e089e1..f1832b970198a 100644 --- a/tests/ui/format_args.stderr +++ b/tests/ui/format_args.stderr @@ -1,5 +1,5 @@ error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:76:72 + --> $DIR/format_args.rs:77:72 | LL | let _ = format!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this @@ -7,136 +7,148 @@ LL | let _ = format!("error: something failed at {}", Location::caller().to_ = note: `-D clippy::to-string-in-format-args` implied by `-D warnings` error: `to_string` applied to a type that implements `Display` in `write!` args - --> $DIR/format_args.rs:80:27 + --> $DIR/format_args.rs:81:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `writeln!` args - --> $DIR/format_args.rs:85:27 + --> $DIR/format_args.rs:86:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `print!` args - --> $DIR/format_args.rs:87:63 + --> $DIR/format_args.rs:88:63 | LL | print!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:88:65 + --> $DIR/format_args.rs:89:65 | LL | println!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprint!` args - --> $DIR/format_args.rs:89:64 + --> $DIR/format_args.rs:90:64 | LL | eprint!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprintln!` args - --> $DIR/format_args.rs:90:66 + --> $DIR/format_args.rs:91:66 | LL | eprintln!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format_args!` args - --> $DIR/format_args.rs:91:77 + --> $DIR/format_args.rs:92:77 | LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert!` args - --> $DIR/format_args.rs:92:70 + --> $DIR/format_args.rs:93:70 | LL | assert!(true, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_eq!` args - --> $DIR/format_args.rs:93:73 + --> $DIR/format_args.rs:94:73 | LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_ne!` args - --> $DIR/format_args.rs:94:73 + --> $DIR/format_args.rs:95:73 | LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `panic!` args - --> $DIR/format_args.rs:95:63 + --> $DIR/format_args.rs:96:63 | LL | panic!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:96:20 + --> $DIR/format_args.rs:97:20 | LL | println!("{}", X(1).to_string()); | ^^^^^^^^^^^^^^^^ help: use this: `*X(1)` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:97:20 + --> $DIR/format_args.rs:98:20 | LL | println!("{}", Y(&X(1)).to_string()); | ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:98:24 + --> $DIR/format_args.rs:99:24 | LL | println!("{}", Z(1).to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:99:20 + --> $DIR/format_args.rs:100:20 | LL | println!("{}", x.to_string()); | ^^^^^^^^^^^^^ help: use this: `**x` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:100:20 + --> $DIR/format_args.rs:101:20 | LL | println!("{}", x_ref.to_string()); | ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:102:39 + --> $DIR/format_args.rs:103:39 | LL | println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:103:52 + --> $DIR/format_args.rs:104:52 | LL | println!("{foo}{bar}", foo = "foo", bar = "bar".to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:104:39 + --> $DIR/format_args.rs:105:39 | LL | println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:105:52 + --> $DIR/format_args.rs:106:52 | LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string()); | ^^^^^^^^^^^^ help: remove this +error: `to_string` applied to a type that implements `Display` in `print!` args + --> $DIR/format_args.rs:118:37 + | +LL | print!("{}", (Location::caller().to_string())); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `print!` args + --> $DIR/format_args.rs:119:39 + | +LL | print!("{}", ((Location::caller()).to_string())); + | ^^^^^^^^^^^^ help: remove this + error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:144:38 + --> $DIR/format_args.rs:147:38 | LL | let x = format!("{} {}", a, b.to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:158:24 + --> $DIR/format_args.rs:161:24 | LL | println!("{}", original[..10].to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]` -error: aborting due to 23 previous errors +error: aborting due to 25 previous errors diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed new file mode 100644 index 0000000000000..1cf49ca45f494 --- /dev/null +++ b/tests/ui/from_over_into.fixed @@ -0,0 +1,87 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![warn(clippy::from_over_into)] +#![allow(unused)] + +// this should throw an error +struct StringWrapper(String); + +impl From for StringWrapper { + fn from(val: String) -> Self { + StringWrapper(val) + } +} + +struct SelfType(String); + +impl From for SelfType { + fn from(val: String) -> Self { + SelfType(String::new()) + } +} + +#[derive(Default)] +struct X; + +impl X { + const FOO: &'static str = "a"; +} + +struct SelfKeywords; + +impl From for SelfKeywords { + fn from(val: X) -> Self { + let _ = X::default(); + let _ = X::FOO; + let _: X = val; + + SelfKeywords + } +} + +struct ExplicitPaths(bool); + +impl core::convert::From for bool { + fn from(mut val: crate::ExplicitPaths) -> Self { + let in_closure = || val.0; + + val.0 = false; + val.0 + } +} + +// this is fine +struct A(String); + +impl From for A { + fn from(s: String) -> A { + A(s) + } +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + struct FromOverInto(Vec); + + impl From> for FromOverInto { + fn from(val: Vec) -> Self { + FromOverInto(val) + } + } +} + +fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 292d0924fb17a..d30f3c3fc9256 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -1,4 +1,8 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::from_over_into)] +#![allow(unused)] // this should throw an error struct StringWrapper(String); @@ -9,6 +13,44 @@ impl Into for String { } } +struct SelfType(String); + +impl Into for String { + fn into(self) -> SelfType { + SelfType(Self::new()) + } +} + +#[derive(Default)] +struct X; + +impl X { + const FOO: &'static str = "a"; +} + +struct SelfKeywords; + +impl Into for X { + fn into(self) -> SelfKeywords { + let _ = Self::default(); + let _ = Self::FOO; + let _: Self = self; + + SelfKeywords + } +} + +struct ExplicitPaths(bool); + +impl core::convert::Into for crate::ExplicitPaths { + fn into(mut self) -> bool { + let in_closure = || self.0; + + self.0 = false; + self.0 + } +} + // this is fine struct A(String); @@ -18,4 +60,28 @@ impl From for A { } } +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 469adadd2196d..9c2a7c04c3646 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,11 +1,75 @@ 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 + --> $DIR/from_over_into.rs:10:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider to implement `From` instead = note: `-D clippy::from-over-into` implied by `-D warnings` +help: replace the `Into` implentation with `From` + | +LL ~ impl From for StringWrapper { +LL ~ fn from(val: String) -> Self { +LL ~ StringWrapper(val) + | + +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:18:1 + | +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From` + | +LL ~ impl From for SelfType { +LL ~ fn from(val: String) -> Self { +LL ~ SelfType(String::new()) + | + +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:33:1 + | +LL | impl Into for X { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From` + | +LL ~ impl From for SelfKeywords { +LL ~ fn from(val: X) -> Self { +LL ~ let _ = X::default(); +LL ~ let _ = X::FOO; +LL ~ let _: X = val; + | + +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:45:1 + | +LL | impl core::convert::Into for crate::ExplicitPaths { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence +help: replace the `Into` implentation with `From` + | +LL ~ impl core::convert::From for bool { +LL ~ fn from(mut val: crate::ExplicitPaths) -> Self { +LL ~ let in_closure = || val.0; +LL | +LL ~ val.0 = false; +LL ~ val.0 + | + +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:80:5 + | +LL | impl Into> for Vec { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From>` + | +LL ~ impl From> for FromOverInto { +LL ~ fn from(val: Vec) -> Self { +LL ~ FromOverInto(val) + | -error: aborting due to previous error +error: aborting due to 5 previous errors diff --git a/tests/ui/from_over_into_unfixable.rs b/tests/ui/from_over_into_unfixable.rs new file mode 100644 index 0000000000000..3b280b7488ae7 --- /dev/null +++ b/tests/ui/from_over_into_unfixable.rs @@ -0,0 +1,35 @@ +#![warn(clippy::from_over_into)] + +struct InMacro(String); + +macro_rules! in_macro { + ($e:ident) => { + $e + }; +} + +impl Into for String { + fn into(self) -> InMacro { + InMacro(in_macro!(self)) + } +} + +struct WeirdUpperSelf; + +impl Into for &'static [u8] { + fn into(self) -> WeirdUpperSelf { + let _ = Self::default(); + WeirdUpperSelf + } +} + +struct ContainsVal; + +impl Into for ContainsVal { + fn into(self) -> u8 { + let val = 1; + val + 1 + } +} + +fn main() {} diff --git a/tests/ui/from_over_into_unfixable.stderr b/tests/ui/from_over_into_unfixable.stderr new file mode 100644 index 0000000000000..6f6ce351921be --- /dev/null +++ b/tests/ui/from_over_into_unfixable.stderr @@ -0,0 +1,29 @@ +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:11:1 + | +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: replace the `Into` implentation with `From` + = note: `-D clippy::from-over-into` implied by `-D warnings` + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:19:1 + | +LL | impl Into for &'static [u8] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: replace the `Into` implentation with `From<&'static [u8]>` + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:28:1 + | +LL | impl Into for ContainsVal { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence + = help: replace the `Into` implentation with `From` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/implicit_saturating_sub.fixed b/tests/ui/implicit_saturating_sub.fixed index e6f57e9267eac..93df81b1a7ff0 100644 --- a/tests/ui/implicit_saturating_sub.fixed +++ b/tests/ui/implicit_saturating_sub.fixed @@ -2,6 +2,21 @@ #![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)] #![warn(clippy::implicit_saturating_sub)] +use std::cmp::PartialEq; +use std::ops::SubAssign; +// Mock type +struct Mock; + +impl PartialEq for Mock { + fn eq(&self, _: &u32) -> bool { + true + } +} + +impl SubAssign for Mock { + fn sub_assign(&mut self, _: u32) {} +} + fn main() { // Tests for unsigned integers @@ -165,4 +180,39 @@ fn main() { } else { println!("side effect"); } + + // Extended tests + let mut m = Mock; + let mut u_32 = 3000; + let a = 200; + let mut _b = 8; + + if m != 0 { + m -= 1; + } + + if a > 0 { + _b -= 1; + } + + if 0 > a { + _b -= 1; + } + + if u_32 > 0 { + u_32 -= 1; + } else { + println!("don't lint this"); + } + + if u_32 > 0 { + println!("don't lint this"); + u_32 -= 1; + } + + if u_32 > 42 { + println!("brace yourself!"); + } else if u_32 > 0 { + u_32 -= 1; + } } diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs index 8bb28d149c628..8340bc8264d58 100644 --- a/tests/ui/implicit_saturating_sub.rs +++ b/tests/ui/implicit_saturating_sub.rs @@ -2,6 +2,21 @@ #![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)] #![warn(clippy::implicit_saturating_sub)] +use std::cmp::PartialEq; +use std::ops::SubAssign; +// Mock type +struct Mock; + +impl PartialEq for Mock { + fn eq(&self, _: &u32) -> bool { + true + } +} + +impl SubAssign for Mock { + fn sub_assign(&mut self, _: u32) {} +} + fn main() { // Tests for unsigned integers @@ -211,4 +226,39 @@ fn main() { } else { println!("side effect"); } + + // Extended tests + let mut m = Mock; + let mut u_32 = 3000; + let a = 200; + let mut _b = 8; + + if m != 0 { + m -= 1; + } + + if a > 0 { + _b -= 1; + } + + if 0 > a { + _b -= 1; + } + + if u_32 > 0 { + u_32 -= 1; + } else { + println!("don't lint this"); + } + + if u_32 > 0 { + println!("don't lint this"); + u_32 -= 1; + } + + if u_32 > 42 { + println!("brace yourself!"); + } else if u_32 > 0 { + u_32 -= 1; + } } diff --git a/tests/ui/implicit_saturating_sub.stderr b/tests/ui/implicit_saturating_sub.stderr index 5bb9a606422a1..5e589d931e431 100644 --- a/tests/ui/implicit_saturating_sub.stderr +++ b/tests/ui/implicit_saturating_sub.stderr @@ -1,5 +1,5 @@ error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:13:5 + --> $DIR/implicit_saturating_sub.rs:28:5 | LL | / if u_8 > 0 { LL | | u_8 = u_8 - 1; @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:20:13 + --> $DIR/implicit_saturating_sub.rs:35:13 | LL | / if u_8 > 0 { LL | | u_8 -= 1; @@ -17,7 +17,7 @@ LL | | } | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:34:5 + --> $DIR/implicit_saturating_sub.rs:49:5 | LL | / if u_16 > 0 { LL | | u_16 -= 1; @@ -25,7 +25,7 @@ LL | | } | |_____^ help: try: `u_16 = u_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:44:5 + --> $DIR/implicit_saturating_sub.rs:59:5 | LL | / if u_32 != 0 { LL | | u_32 -= 1; @@ -33,7 +33,7 @@ LL | | } | |_____^ help: try: `u_32 = u_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:65:5 + --> $DIR/implicit_saturating_sub.rs:80:5 | LL | / if u_64 > 0 { LL | | u_64 -= 1; @@ -41,7 +41,7 @@ LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:70:5 + --> $DIR/implicit_saturating_sub.rs:85:5 | LL | / if 0 < u_64 { LL | | u_64 -= 1; @@ -49,7 +49,7 @@ LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:75:5 + --> $DIR/implicit_saturating_sub.rs:90:5 | LL | / if 0 != u_64 { LL | | u_64 -= 1; @@ -57,7 +57,7 @@ LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:96:5 + --> $DIR/implicit_saturating_sub.rs:111:5 | LL | / if u_usize > 0 { LL | | u_usize -= 1; @@ -65,7 +65,7 @@ LL | | } | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:108:5 + --> $DIR/implicit_saturating_sub.rs:123:5 | LL | / if i_8 > i8::MIN { LL | | i_8 -= 1; @@ -73,7 +73,7 @@ LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:113:5 + --> $DIR/implicit_saturating_sub.rs:128:5 | LL | / if i_8 > i8::MIN { LL | | i_8 -= 1; @@ -81,7 +81,7 @@ LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:118:5 + --> $DIR/implicit_saturating_sub.rs:133:5 | LL | / if i_8 != i8::MIN { LL | | i_8 -= 1; @@ -89,7 +89,7 @@ LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:123:5 + --> $DIR/implicit_saturating_sub.rs:138:5 | LL | / if i_8 != i8::MIN { LL | | i_8 -= 1; @@ -97,7 +97,7 @@ LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:133:5 + --> $DIR/implicit_saturating_sub.rs:148:5 | LL | / if i_16 > i16::MIN { LL | | i_16 -= 1; @@ -105,7 +105,7 @@ LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:138:5 + --> $DIR/implicit_saturating_sub.rs:153:5 | LL | / if i_16 > i16::MIN { LL | | i_16 -= 1; @@ -113,7 +113,7 @@ LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:143:5 + --> $DIR/implicit_saturating_sub.rs:158:5 | LL | / if i_16 != i16::MIN { LL | | i_16 -= 1; @@ -121,7 +121,7 @@ LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:148:5 + --> $DIR/implicit_saturating_sub.rs:163:5 | LL | / if i_16 != i16::MIN { LL | | i_16 -= 1; @@ -129,7 +129,7 @@ LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:158:5 + --> $DIR/implicit_saturating_sub.rs:173:5 | LL | / if i_32 > i32::MIN { LL | | i_32 -= 1; @@ -137,7 +137,7 @@ LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:163:5 + --> $DIR/implicit_saturating_sub.rs:178:5 | LL | / if i_32 > i32::MIN { LL | | i_32 -= 1; @@ -145,7 +145,7 @@ LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:168:5 + --> $DIR/implicit_saturating_sub.rs:183:5 | LL | / if i_32 != i32::MIN { LL | | i_32 -= 1; @@ -153,7 +153,7 @@ LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:173:5 + --> $DIR/implicit_saturating_sub.rs:188:5 | LL | / if i_32 != i32::MIN { LL | | i_32 -= 1; @@ -161,7 +161,7 @@ LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:183:5 + --> $DIR/implicit_saturating_sub.rs:198:5 | LL | / if i64::MIN < i_64 { LL | | i_64 -= 1; @@ -169,7 +169,7 @@ LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:188:5 + --> $DIR/implicit_saturating_sub.rs:203:5 | LL | / if i64::MIN != i_64 { LL | | i_64 -= 1; @@ -177,7 +177,7 @@ LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:193:5 + --> $DIR/implicit_saturating_sub.rs:208:5 | LL | / if i64::MIN < i_64 { LL | | i_64 -= 1; diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index 0cadd5a3da198..1a646e49ce3ae 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -40,3 +40,10 @@ fn main() { let ok26 = 0x6_A0_BF; let ok27 = 0b1_0010_0101; } + +fn issue9651() { + // lint but octal form is not possible here + let _ = 08; + let _ = 09; + let _ = 089; +} diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index 365b240747352..603d47bacca80 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -135,5 +135,38 @@ error: digits of hex or binary literal not grouped by four LL | let fail25 = 0b01_100_101; | ^^^^^^^^^^^^ help: consider: `0b0110_0101` -error: aborting due to 18 previous errors +error: this is a decimal constant + --> $DIR/literals.rs:46:13 + | +LL | let _ = 08; + | ^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 8; + | ~ + +error: this is a decimal constant + --> $DIR/literals.rs:47:13 + | +LL | let _ = 09; + | ^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 9; + | ~ + +error: this is a decimal constant + --> $DIR/literals.rs:48:13 + | +LL | let _ = 089; + | ^^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 89; + | ~~ + +error: aborting due to 21 previous errors diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed index 26e3b8f63e700..c9a819ba53544 100644 --- a/tests/ui/manual_assert.edition2018.fixed +++ b/tests/ui/manual_assert.edition2018.fixed @@ -1,6 +1,6 @@ // revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 // run-rustfix #![warn(clippy::manual_assert)] @@ -29,7 +29,9 @@ fn main() { panic!("qaqaq{:?}", a); } assert!(a.is_empty(), "qaqaq{:?}", a); - assert!(a.is_empty(), "qwqwq"); + if !a.is_empty() { + panic!("qwqwq"); + } if a.len() == 3 { println!("qwq"); println!("qwq"); @@ -44,21 +46,32 @@ fn main() { println!("qwq"); } let b = vec![1, 2, 3]; - assert!(!b.is_empty(), "panic1"); - assert!(!(b.is_empty() && a.is_empty()), "panic2"); - assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - assert!(!(b.is_empty() || a.is_empty()), "panic4"); - assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + if b.is_empty() { + panic!("panic1"); + } + if b.is_empty() && a.is_empty() { + panic!("panic2"); + } + if a.is_empty() && !b.is_empty() { + panic!("panic3"); + } + if b.is_empty() || a.is_empty() { + panic!("panic4"); + } + if a.is_empty() || !b.is_empty() { + panic!("panic5"); + } assert!(!a.is_empty(), "with expansion {}", one!()); } fn issue7730(a: u8) { // Suggestion should preserve comment - // comment -/* this is a + if a > 2 { + // comment + /* this is a multiline comment */ -/// Doc comment -// comment after `panic!` -assert!(!(a > 2), "panic with comment"); + /// Doc comment + panic!("panic with comment") // comment after `panic!` + } } diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 237638ee1344c..1f2e1e3087bd0 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -8,54 +8,6 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:34:5 - | -LL | / if !a.is_empty() { -LL | | panic!("qwqwq"); -LL | | } - | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:51:5 - | -LL | / if b.is_empty() { -LL | | panic!("panic1"); -LL | | } - | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:54:5 - | -LL | / if b.is_empty() && a.is_empty() { -LL | | panic!("panic2"); -LL | | } - | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:57:5 - | -LL | / if a.is_empty() && !b.is_empty() { -LL | | panic!("panic3"); -LL | | } - | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:60:5 - | -LL | / if b.is_empty() || a.is_empty() { -LL | | panic!("panic4"); -LL | | } - | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:63:5 - | -LL | / if a.is_empty() || !b.is_empty() { -LL | | panic!("panic5"); -LL | | } - | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` - error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 | @@ -64,22 +16,5 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:73:5 - | -LL | / if a > 2 { -LL | | // comment -LL | | /* this is a -LL | | multiline -... | -LL | | panic!("panic with comment") // comment after `panic!` -LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a > 2), "panic with comment"); - | - -error: aborting due to 9 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed index 26e3b8f63e700..2f62de51cadcd 100644 --- a/tests/ui/manual_assert.edition2021.fixed +++ b/tests/ui/manual_assert.edition2021.fixed @@ -1,6 +1,6 @@ // revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 // run-rustfix #![warn(clippy::manual_assert)] diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index 8c37753071dfb..6a4cc2468d419 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -1,6 +1,6 @@ // revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 // run-rustfix #![warn(clippy::manual_assert)] diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs index 54fd888af99fa..331fd29b74e8e 100644 --- a/tests/ui/manual_clamp.rs +++ b/tests/ui/manual_clamp.rs @@ -1,3 +1,4 @@ +#![feature(custom_inner_attributes)] #![warn(clippy::manual_clamp)] #![allow( unused, @@ -302,3 +303,29 @@ fn dont_tell_me_what_to_do() { fn cmp_min_max(input: i32) -> i32 { input * 3 } + +fn msrv_1_49() { + #![clippy::msrv = "1.49"] + + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr index 0604f8606c3f3..70abe28091c94 100644 --- a/tests/ui/manual_clamp.stderr +++ b/tests/ui/manual_clamp.stderr @@ -1,5 +1,5 @@ error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:76:5 + --> $DIR/manual_clamp.rs:77:5 | LL | / if x9 < min { LL | | x9 = min; @@ -13,7 +13,7 @@ LL | | } = note: `-D clippy::manual-clamp` implied by `-D warnings` error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:91:5 + --> $DIR/manual_clamp.rs:92:5 | LL | / if x11 > max { LL | | x11 = max; @@ -26,7 +26,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:99:5 + --> $DIR/manual_clamp.rs:100:5 | LL | / if min > x12 { LL | | x12 = min; @@ -39,7 +39,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:107:5 + --> $DIR/manual_clamp.rs:108:5 | LL | / if max < x13 { LL | | x13 = max; @@ -52,7 +52,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:161:5 + --> $DIR/manual_clamp.rs:162:5 | LL | / if max < x33 { LL | | x33 = max; @@ -65,7 +65,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:21:14 + --> $DIR/manual_clamp.rs:22:14 | LL | let x0 = if max < input { | ______________^ @@ -80,7 +80,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:29:14 + --> $DIR/manual_clamp.rs:30:14 | LL | let x1 = if input > max { | ______________^ @@ -95,7 +95,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:37:14 + --> $DIR/manual_clamp.rs:38:14 | LL | let x2 = if input < min { | ______________^ @@ -110,7 +110,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:45:14 + --> $DIR/manual_clamp.rs:46:14 | LL | let x3 = if min > input { | ______________^ @@ -125,7 +125,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:53:14 + --> $DIR/manual_clamp.rs:54:14 | LL | let x4 = input.max(min).min(max); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -133,7 +133,7 @@ LL | let x4 = input.max(min).min(max); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:55:14 + --> $DIR/manual_clamp.rs:56:14 | LL | let x5 = input.min(max).max(min); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -141,7 +141,7 @@ LL | let x5 = input.min(max).max(min); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:57:14 + --> $DIR/manual_clamp.rs:58:14 | LL | let x6 = match input { | ______________^ @@ -154,7 +154,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:63:14 + --> $DIR/manual_clamp.rs:64:14 | LL | let x7 = match input { | ______________^ @@ -167,7 +167,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:69:14 + --> $DIR/manual_clamp.rs:70:14 | LL | let x8 = match input { | ______________^ @@ -180,7 +180,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:83:15 + --> $DIR/manual_clamp.rs:84:15 | LL | let x10 = match input { | _______________^ @@ -193,7 +193,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:114:15 + --> $DIR/manual_clamp.rs:115:15 | LL | let x14 = if input > CONST_MAX { | _______________^ @@ -208,7 +208,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:123:19 + --> $DIR/manual_clamp.rs:124:19 | LL | let x15 = if input > max { | ___________________^ @@ -224,7 +224,7 @@ LL | | }; = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:134:19 + --> $DIR/manual_clamp.rs:135:19 | LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -232,7 +232,7 @@ LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:135:19 + --> $DIR/manual_clamp.rs:136:19 | LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -240,7 +240,7 @@ LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:136:19 + --> $DIR/manual_clamp.rs:137:19 | LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -248,7 +248,7 @@ LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:137:19 + --> $DIR/manual_clamp.rs:138:19 | LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -256,7 +256,7 @@ LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:138:19 + --> $DIR/manual_clamp.rs:139:19 | LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -264,7 +264,7 @@ LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:139:19 + --> $DIR/manual_clamp.rs:140:19 | LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -272,7 +272,7 @@ LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:140:19 + --> $DIR/manual_clamp.rs:141:19 | LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -280,7 +280,7 @@ LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:141:19 + --> $DIR/manual_clamp.rs:142:19 | LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -288,7 +288,7 @@ LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:143:19 + --> $DIR/manual_clamp.rs:144:19 | LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -297,7 +297,7 @@ LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:144:19 + --> $DIR/manual_clamp.rs:145:19 | LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -306,7 +306,7 @@ LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:145:19 + --> $DIR/manual_clamp.rs:146:19 | LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -315,7 +315,7 @@ LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:146:19 + --> $DIR/manual_clamp.rs:147:19 | LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -324,7 +324,7 @@ LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:147:19 + --> $DIR/manual_clamp.rs:148:19 | LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -333,7 +333,7 @@ LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:148:19 + --> $DIR/manual_clamp.rs:149:19 | LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -342,7 +342,7 @@ LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:149:19 + --> $DIR/manual_clamp.rs:150:19 | LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -351,7 +351,7 @@ LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:150:19 + --> $DIR/manual_clamp.rs:151:19 | LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -360,7 +360,7 @@ LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:153:5 + --> $DIR/manual_clamp.rs:154:5 | LL | / if x32 < min { LL | | x32 = min; @@ -371,5 +371,20 @@ LL | | } | = note: clamp will panic if max < min -error: aborting due to 34 previous errors +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:324:13 + | +LL | let _ = if input < min { + | _____________^ +LL | | min +LL | | } else if input > max { +LL | | max +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: aborting due to 35 previous errors diff --git a/tests/ui/manual_filter.fixed b/tests/ui/manual_filter.fixed new file mode 100644 index 0000000000000..3553291b87df6 --- /dev/null +++ b/tests/ui/manual_filter.fixed @@ -0,0 +1,119 @@ +// run-rustfix + +#![warn(clippy::manual_filter)] +#![allow(unused_variables, dead_code)] + +fn main() { + Some(0).filter(|&x| x <= 0); + + Some(1).filter(|&x| x <= 0); + + Some(2).filter(|&x| x <= 0); + + Some(3).filter(|&x| x > 0); + + let y = Some(4); + y.filter(|&x| x <= 0); + + Some(5).filter(|&x| x > 0); + + Some(6).as_ref().filter(|&x| x > &0); + + let external_cond = true; + Some(String::new()).filter(|x| external_cond); + + Some(7).filter(|&x| external_cond); + + Some(8).filter(|&x| x != 0); + + Some(9).filter(|&x| x > 10 && x < 100); + + const fn f1() { + // Don't lint, `.filter` is not const + match Some(10) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + } + + #[allow(clippy::blocks_in_if_conditions)] + Some(11).filter(|&x| { + println!("foo"); + x > 10 && x < 100 + }); + + match Some(12) { + // Don't Lint, statement is lost by `.filter` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(13) { + // Don't Lint, because of `None => Some(1)` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => Some(1), + }; + + unsafe fn f(x: u32) -> bool { + true + } + let _ = Some(14).filter(|&x| unsafe { f(x) }); + let _ = Some(15).filter(|&x| unsafe { f(x) }); + + #[allow(clippy::redundant_pattern_matching)] + if let Some(_) = Some(16) { + Some(16) + } else { Some(16).filter(|&x| x % 2 == 0) }; + + match Some((17, 17)) { + // Not linted for now could be + Some((x, y)) => { + if y != x { + Some((x, y)) + } else { + None + } + }, + None => None, + }; + + struct NamedTuple { + pub x: u8, + pub y: (i32, u32), + } + + match Some(NamedTuple { + // Not linted for now could be + x: 17, + y: (18, 19), + }) { + Some(NamedTuple { x, y }) => { + if y.1 != x as u32 { + Some(NamedTuple { x, y }) + } else { + None + } + }, + None => None, + }; +} diff --git a/tests/ui/manual_filter.rs b/tests/ui/manual_filter.rs new file mode 100644 index 0000000000000..aa9f90f752b17 --- /dev/null +++ b/tests/ui/manual_filter.rs @@ -0,0 +1,243 @@ +// run-rustfix + +#![warn(clippy::manual_filter)] +#![allow(unused_variables, dead_code)] + +fn main() { + match Some(0) { + None => None, + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + }; + + match Some(1) { + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + None => None, + }; + + match Some(2) { + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + _ => None, + }; + + match Some(3) { + Some(x) => { + if x > 0 { + Some(x) + } else { + None + } + }, + None => None, + }; + + let y = Some(4); + match y { + // Some(4) + None => None, + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + }; + + match Some(5) { + Some(x) => { + if x > 0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + match Some(6) { + Some(ref x) => { + if x > &0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + let external_cond = true; + match Some(String::new()) { + Some(x) => { + if external_cond { + Some(x) + } else { + None + } + }, + _ => None, + }; + + if let Some(x) = Some(7) { + if external_cond { Some(x) } else { None } + } else { + None + }; + + match &Some(8) { + &Some(x) => { + if x != 0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + match Some(9) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + + const fn f1() { + // Don't lint, `.filter` is not const + match Some(10) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + } + + #[allow(clippy::blocks_in_if_conditions)] + match Some(11) { + // Lint, statement is preserved by `.filter` + Some(x) => { + if { + println!("foo"); + x > 10 && x < 100 + } { + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(12) { + // Don't Lint, statement is lost by `.filter` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(13) { + // Don't Lint, because of `None => Some(1)` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => Some(1), + }; + + unsafe fn f(x: u32) -> bool { + true + } + let _ = match Some(14) { + Some(x) => { + if unsafe { f(x) } { + Some(x) + } else { + None + } + }, + None => None, + }; + let _ = match Some(15) { + Some(x) => unsafe { + if f(x) { Some(x) } else { None } + }, + None => None, + }; + + #[allow(clippy::redundant_pattern_matching)] + if let Some(_) = Some(16) { + Some(16) + } else if let Some(x) = Some(16) { + // Lint starting from here + if x % 2 == 0 { Some(x) } else { None } + } else { + None + }; + + match Some((17, 17)) { + // Not linted for now could be + Some((x, y)) => { + if y != x { + Some((x, y)) + } else { + None + } + }, + None => None, + }; + + struct NamedTuple { + pub x: u8, + pub y: (i32, u32), + } + + match Some(NamedTuple { + // Not linted for now could be + x: 17, + y: (18, 19), + }) { + Some(NamedTuple { x, y }) => { + if y.1 != x as u32 { + Some(NamedTuple { x, y }) + } else { + None + } + }, + None => None, + }; +} diff --git a/tests/ui/manual_filter.stderr b/tests/ui/manual_filter.stderr new file mode 100644 index 0000000000000..53dea9229306b --- /dev/null +++ b/tests/ui/manual_filter.stderr @@ -0,0 +1,191 @@ +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:7:5 + | +LL | / match Some(0) { +LL | | None => None, +LL | | Some(x) => { +LL | | if x > 0 { +... | +LL | | }, +LL | | }; + | |_____^ help: try this: `Some(0).filter(|&x| x <= 0)` + | + = note: `-D clippy::manual-filter` implied by `-D warnings` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:18:5 + | +LL | / match Some(1) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | None +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(1).filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:29:5 + | +LL | / match Some(2) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | None +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(2).filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:40:5 + | +LL | / match Some(3) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(3).filter(|&x| x > 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:52:5 + | +LL | / match y { +LL | | // Some(4) +LL | | None => None, +LL | | Some(x) => { +... | +LL | | }, +LL | | }; + | |_____^ help: try this: `y.filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:64:5 + | +LL | / match Some(5) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(5).filter(|&x| x > 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:75:5 + | +LL | / match Some(6) { +LL | | Some(ref x) => { +LL | | if x > &0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(6).as_ref().filter(|&x| x > &0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:87:5 + | +LL | / match Some(String::new()) { +LL | | Some(x) => { +LL | | if external_cond { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).filter(|x| external_cond)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:98:5 + | +LL | / if let Some(x) = Some(7) { +LL | | if external_cond { Some(x) } else { None } +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `Some(7).filter(|&x| external_cond)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:104:5 + | +LL | / match &Some(8) { +LL | | &Some(x) => { +LL | | if x != 0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(8).filter(|&x| x != 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:115:5 + | +LL | / match Some(9) { +LL | | Some(x) => { +LL | | if x > 10 && x < 100 { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(9).filter(|&x| x > 10 && x < 100)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:141:5 + | +LL | / match Some(11) { +LL | | // Lint, statement is preserved by `.filter` +LL | | Some(x) => { +LL | | if { +... | +LL | | None => None, +LL | | }; + | |_____^ + | +help: try this + | +LL ~ Some(11).filter(|&x| { +LL + println!("foo"); +LL + x > 10 && x < 100 +LL ~ }); + | + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:185:13 + | +LL | let _ = match Some(14) { + | _____________^ +LL | | Some(x) => { +LL | | if unsafe { f(x) } { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(14).filter(|&x| unsafe { f(x) })` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:195:13 + | +LL | let _ = match Some(15) { + | _____________^ +LL | | Some(x) => unsafe { +LL | | if f(x) { Some(x) } else { None } +LL | | }, +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(15).filter(|&x| unsafe { f(x) })` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:205:12 + | +LL | } else if let Some(x) = Some(16) { + | ____________^ +LL | | // Lint starting from here +LL | | if x % 2 == 0 { Some(x) } else { None } +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `{ Some(16).filter(|&x| x % 2 == 0) }` + +error: aborting due to 15 previous errors + diff --git a/tests/ui/manual_rem_euclid.fixed b/tests/ui/manual_rem_euclid.fixed index 5601c96c10b28..b942fbfe93056 100644 --- a/tests/ui/manual_rem_euclid.fixed +++ b/tests/ui/manual_rem_euclid.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs +#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -53,3 +54,32 @@ pub fn rem_euclid_4(num: i32) -> i32 { pub const fn const_rem_euclid_4(num: i32) -> i32 { num.rem_euclid(4) } + +pub fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub fn msrv_1_38() { + #![clippy::msrv = "1.38"] + + let x: i32 = 10; + let _: i32 = x.rem_euclid(4); +} + +// For const fns: +pub const fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub const fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + let x: i32 = 10; + let _: i32 = x.rem_euclid(4); +} diff --git a/tests/ui/manual_rem_euclid.rs b/tests/ui/manual_rem_euclid.rs index 52135be26b73c..7462d532169f6 100644 --- a/tests/ui/manual_rem_euclid.rs +++ b/tests/ui/manual_rem_euclid.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs +#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -53,3 +54,32 @@ pub fn rem_euclid_4(num: i32) -> i32 { pub const fn const_rem_euclid_4(num: i32) -> i32 { ((num % 4) + 4) % 4 } + +pub fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub fn msrv_1_38() { + #![clippy::msrv = "1.38"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +// For const fns: +pub const fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub const fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} diff --git a/tests/ui/manual_rem_euclid.stderr b/tests/ui/manual_rem_euclid.stderr index a237fd0213c1e..d51bac03b565a 100644 --- a/tests/ui/manual_rem_euclid.stderr +++ b/tests/ui/manual_rem_euclid.stderr @@ -1,5 +1,5 @@ error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:19:18 + --> $DIR/manual_rem_euclid.rs:20:18 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -7,31 +7,31 @@ LL | let _: i32 = ((value % 4) + 4) % 4; = note: `-D clippy::manual-rem-euclid` implied by `-D warnings` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:20:18 + --> $DIR/manual_rem_euclid.rs:21:18 | LL | let _: i32 = (4 + (value % 4)) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:21:18 + --> $DIR/manual_rem_euclid.rs:22:18 | LL | let _: i32 = (value % 4 + 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:22:18 + --> $DIR/manual_rem_euclid.rs:23:18 | LL | let _: i32 = (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:23:22 + --> $DIR/manual_rem_euclid.rs:24:22 | LL | let _: i32 = 1 + (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:12:22 + --> $DIR/manual_rem_euclid.rs:13:22 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -42,16 +42,28 @@ LL | internal_rem_euclid!(); = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info) error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:49:5 + --> $DIR/manual_rem_euclid.rs:50:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:54:5 + --> $DIR/manual_rem_euclid.rs:55:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` -error: aborting due to 8 previous errors +error: manual `rem_euclid` implementation + --> $DIR/manual_rem_euclid.rs:69:18 + | +LL | let _: i32 = ((x % 4) + 4) % 4; + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` + +error: manual `rem_euclid` implementation + --> $DIR/manual_rem_euclid.rs:84:18 + | +LL | let _: i32 = ((x % 4) + 4) % 4; + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` + +error: aborting due to 10 previous errors diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs index cbb84eb5c7e37..85009d78558ba 100644 --- a/tests/ui/manual_strip.rs +++ b/tests/ui/manual_strip.rs @@ -1,3 +1,4 @@ +#![feature(custom_inner_attributes)] #![warn(clippy::manual_strip)] fn main() { @@ -64,3 +65,21 @@ fn main() { s4[2..].to_string(); } } + +fn msrv_1_44() { + #![clippy::msrv = "1.44"] + + let s = "abc"; + if s.starts_with('a') { + s[1..].to_string(); + } +} + +fn msrv_1_45() { + #![clippy::msrv = "1.45"] + + let s = "abc"; + if s.starts_with('a') { + s[1..].to_string(); + } +} diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr index 2191ccb85dd5d..ad2a362f3e76d 100644 --- a/tests/ui/manual_strip.stderr +++ b/tests/ui/manual_strip.stderr @@ -1,11 +1,11 @@ error: stripping a prefix manually - --> $DIR/manual_strip.rs:7:24 + --> $DIR/manual_strip.rs:8:24 | LL | str::to_string(&s["ab".len()..]); | ^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:6:5 + --> $DIR/manual_strip.rs:7:5 | LL | if s.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL ~ .to_string(); | error: stripping a suffix manually - --> $DIR/manual_strip.rs:15:24 + --> $DIR/manual_strip.rs:16:24 | LL | str::to_string(&s[..s.len() - "bc".len()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the suffix was tested here - --> $DIR/manual_strip.rs:14:5 + --> $DIR/manual_strip.rs:15:5 | LL | if s.ends_with("bc") { | ^^^^^^^^^^^^^^^^^^^^^ @@ -42,13 +42,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:24:24 + --> $DIR/manual_strip.rs:25:24 | LL | str::to_string(&s[1..]); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:23:5 + --> $DIR/manual_strip.rs:24:5 | LL | if s.starts_with('a') { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -60,13 +60,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:31:24 + --> $DIR/manual_strip.rs:32:24 | LL | str::to_string(&s[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:30:5 + --> $DIR/manual_strip.rs:31:5 | LL | if s.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,13 +77,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:37:24 + --> $DIR/manual_strip.rs:38:24 | LL | str::to_string(&s[PREFIX.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:36:5 + --> $DIR/manual_strip.rs:37:5 | LL | if s.starts_with(PREFIX) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -95,13 +95,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:44:24 + --> $DIR/manual_strip.rs:45:24 | LL | str::to_string(&TARGET[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:43:5 + --> $DIR/manual_strip.rs:44:5 | LL | if TARGET.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,13 +112,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:50:9 + --> $DIR/manual_strip.rs:51:9 | LL | s1[2..].to_uppercase(); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:49:5 + --> $DIR/manual_strip.rs:50:5 | LL | if s1.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,5 +128,22 @@ LL ~ if let Some() = s1.strip_prefix("ab") { LL ~ .to_uppercase(); | -error: aborting due to 7 previous errors +error: stripping a prefix manually + --> $DIR/manual_strip.rs:83:9 + | +LL | s[1..].to_string(); + | ^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:82:5 + | +LL | if s.starts_with('a') { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL ~ if let Some() = s.strip_prefix('a') { +LL ~ .to_string(); + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 5429fb4e454e3..396b22a9abb39 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,6 +1,8 @@ // aux-build:option_helpers.rs + +#![feature(custom_inner_attributes)] #![warn(clippy::map_unwrap_or)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)] #[macro_use] extern crate option_helpers; @@ -79,3 +81,19 @@ fn main() { option_methods(); result_methods(); } + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let res: Result = Ok(1); + + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let res: Result = Ok(1); + + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); +} diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index abc9c1ece327a..d17d24a403ea5 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,5 +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:16:13 + --> $DIR/map_unwrap_or.rs:18:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -15,7 +15,7 @@ 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 + --> $DIR/map_unwrap_or.rs:22:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -33,7 +33,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:24:13 + --> $DIR/map_unwrap_or.rs:26:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -50,7 +50,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:29:13 + --> $DIR/map_unwrap_or.rs:31:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,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:31:13 + --> $DIR/map_unwrap_or.rs:33:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -80,7 +80,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:35:13 + --> $DIR/map_unwrap_or.rs:37:13 | LL | let _ = opt | _____________^ @@ -95,7 +95,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:46:13 + --> $DIR/map_unwrap_or.rs:48:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +107,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:50:13 + --> $DIR/map_unwrap_or.rs:52:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -117,7 +117,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:54:13 + --> $DIR/map_unwrap_or.rs:56:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -127,7 +127,7 @@ 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:66:13 + --> $DIR/map_unwrap_or.rs:68:13 | LL | let _ = res.map(|x| { | _____________^ @@ -137,7 +137,7 @@ 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 + --> $DIR/map_unwrap_or.rs:72:13 | LL | let _ = res.map(|x| x + 1) | _____________^ @@ -146,5 +146,11 @@ LL | | 0 LL | | }); | |__________^ -error: aborting due to 11 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:98: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 12 previous errors diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 95ca571d07bfb..2498007694c56 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] @@ -193,3 +194,18 @@ fn main() { _ => false, }; } + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn msrv_1_42() { + #![clippy::msrv = "1.42"] + + let _y = matches!(Some(5), Some(0)); +} diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index 3b9c8cadadcc4..b4e48499bd0fb 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] @@ -234,3 +235,21 @@ fn main() { _ => false, }; } + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn msrv_1_42() { + #![clippy::msrv = "1.42"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index e94555e27448b..f1d1c23aeb0de 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:10:14 + --> $DIR/match_expr_like_matches_macro.rs:11:14 | LL | let _y = match x { | ______________^ @@ -11,7 +11,7 @@ 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 + --> $DIR/match_expr_like_matches_macro.rs:17:14 | LL | let _w = match x { | ______________^ @@ -21,7 +21,7 @@ LL | | }; | |_____^ help: try this: `matches!(x, Some(_))` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:22:14 + --> $DIR/match_expr_like_matches_macro.rs:23:14 | LL | let _z = match x { | ______________^ @@ -33,7 +33,7 @@ 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:28:15 + --> $DIR/match_expr_like_matches_macro.rs:29:15 | LL | let _zz = match x { | _______________^ @@ -43,13 +43,13 @@ 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:34:16 + --> $DIR/match_expr_like_matches_macro.rs:35:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:58:20 + --> $DIR/match_expr_like_matches_macro.rs:59:20 | LL | let _ans = match x { | ____________________^ @@ -60,7 +60,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:68:20 + --> $DIR/match_expr_like_matches_macro.rs:69:20 | LL | let _ans = match x { | ____________________^ @@ -73,7 +73,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:78:20 + --> $DIR/match_expr_like_matches_macro.rs:79:20 | LL | let _ans = match x { | ____________________^ @@ -84,7 +84,7 @@ LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:138:18 + --> $DIR/match_expr_like_matches_macro.rs:139:18 | LL | let _z = match &z { | __________________^ @@ -94,7 +94,7 @@ LL | | }; | |_________^ help: try this: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:147:18 + --> $DIR/match_expr_like_matches_macro.rs:148:18 | LL | let _z = match &z { | __________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: try this: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:164:21 + --> $DIR/match_expr_like_matches_macro.rs:165:21 | LL | let _ = match &z { | _____________________^ @@ -114,7 +114,7 @@ LL | | }; | |_____________^ help: try this: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:178:20 + --> $DIR/match_expr_like_matches_macro.rs:179:20 | LL | let _res = match &val { | ____________________^ @@ -124,7 +124,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:190:20 + --> $DIR/match_expr_like_matches_macro.rs:191:20 | LL | let _res = match &val { | ____________________^ @@ -133,5 +133,15 @@ LL | | _ => false, LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` -error: aborting due to 13 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:251:14 + | +LL | let _y = match Some(5) { + | ______________^ +LL | | Some(0) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `matches!(Some(5), Some(0))` + +error: aborting due to 14 previous errors diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 22b04b208f87b..b4097fa96045e 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -1,5 +1,4 @@ #![feature(exclusive_range_pattern)] - #![warn(clippy::match_overlapping_arm)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::if_same_then_else, clippy::equatable_if_let)] diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr index a72becbeb669b..b98d4799e42ca 100644 --- a/tests/ui/match_overlapping_arm.stderr +++ b/tests/ui/match_overlapping_arm.stderr @@ -1,96 +1,96 @@ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:13:9 + --> $DIR/match_overlapping_arm.rs:12:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:14:9 + --> $DIR/match_overlapping_arm.rs:13:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ = note: `-D clippy::match-overlapping-arm` implied by `-D warnings` error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:19:9 + --> $DIR/match_overlapping_arm.rs:18:9 | LL | 0..=5 => println!("0..=5"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:21:9 + --> $DIR/match_overlapping_arm.rs:20:9 | LL | FOO..=11 => println!("FOO..=11"), | ^^^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:56:9 + --> $DIR/match_overlapping_arm.rs:55:9 | LL | 0..11 => println!("0..11"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:57:9 + --> $DIR/match_overlapping_arm.rs:56:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:81:9 + --> $DIR/match_overlapping_arm.rs:80:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:80:9 + --> $DIR/match_overlapping_arm.rs:79:9 | LL | 5..14 => println!("5..14"), | ^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:86:9 + --> $DIR/match_overlapping_arm.rs:85:9 | LL | 0..7 => println!("0..7"), | ^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:87:9 + --> $DIR/match_overlapping_arm.rs:86:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:98:9 + --> $DIR/match_overlapping_arm.rs:97:9 | LL | ..=23 => println!("..=23"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:99:9 + --> $DIR/match_overlapping_arm.rs:98:9 | LL | ..26 => println!("..26"), | ^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:107:9 + --> $DIR/match_overlapping_arm.rs:106:9 | LL | 21..=30 => (), | ^^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:108:9 + --> $DIR/match_overlapping_arm.rs:107:9 | LL | 21..=40 => (), | ^^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:121:9 + --> $DIR/match_overlapping_arm.rs:120:9 | LL | 0..=0x0000_0000_0000_00ff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:122:9 + --> $DIR/match_overlapping_arm.rs:121:9 | LL | 0..=0x0000_0000_0000_ffff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 951f552eb32b6..a6e315e4773a2 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -124,3 +124,12 @@ fn issue_8723() { let _ = val; } + +#[allow(dead_code)] +fn issue_9575() { + fn side_effects() {} + let _ = || { + side_effects(); + println!("Needs curlies"); + }; +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 19c0fee8fd688..cecbd703e5669 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -140,3 +140,11 @@ fn issue_8723() { let _ = val; } + +#[allow(dead_code)] +fn issue_9575() { + fn side_effects() {} + let _ = || match side_effects() { + _ => println!("Needs curlies"), + }; +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 5d4e7314b2137..2b9ec7ee7026e 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -196,5 +196,22 @@ LL + suf LL ~ }; | -error: aborting due to 13 previous errors +error: this match could be replaced by its scrutinee and body + --> $DIR/match_single_binding.rs:147:16 + | +LL | let _ = || match side_effects() { + | ________________^ +LL | | _ => println!("Needs curlies"), +LL | | }; + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ let _ = || { +LL + side_effects(); +LL + println!("Needs curlies"); +LL ~ }; + | + +error: aborting due to 14 previous errors diff --git a/tests/ui/match_wild_err_arm.edition2021.stderr b/tests/ui/match_wild_err_arm.edition2021.stderr deleted file mode 100644 index 525533bf07bb0..0000000000000 --- a/tests/ui/match_wild_err_arm.edition2021.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:14:9 - | -LL | Err(_) => panic!("err"), - | ^^^^^^ - | - = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable - = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` - -error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:20:9 - | -LL | Err(_) => panic!(), - | ^^^^^^ - | - = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable - -error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:26:9 - | -LL | Err(_) => { - | ^^^^^^ - | - = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable - -error: `Err(_e)` matches all errors - --> $DIR/match_wild_err_arm.rs:34:9 - | -LL | Err(_e) => panic!(), - | ^^^^^^^ - | - = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable - -error: aborting due to 4 previous errors - diff --git a/tests/ui/match_wild_err_arm.rs b/tests/ui/match_wild_err_arm.rs index 0a86144b95d5b..823be65efe065 100644 --- a/tests/ui/match_wild_err_arm.rs +++ b/tests/ui/match_wild_err_arm.rs @@ -1,6 +1,3 @@ -// revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 #![feature(exclusive_range_pattern)] #![allow(clippy::match_same_arms)] #![warn(clippy::match_wild_err_arm)] diff --git a/tests/ui/match_wild_err_arm.edition2018.stderr b/tests/ui/match_wild_err_arm.stderr similarity index 86% rename from tests/ui/match_wild_err_arm.edition2018.stderr rename to tests/ui/match_wild_err_arm.stderr index 525533bf07bb0..b016d682698c8 100644 --- a/tests/ui/match_wild_err_arm.edition2018.stderr +++ b/tests/ui/match_wild_err_arm.stderr @@ -1,5 +1,5 @@ error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:14:9 + --> $DIR/match_wild_err_arm.rs:11:9 | LL | Err(_) => panic!("err"), | ^^^^^^ @@ -8,7 +8,7 @@ LL | Err(_) => panic!("err"), = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:20:9 + --> $DIR/match_wild_err_arm.rs:17:9 | LL | Err(_) => panic!(), | ^^^^^^ @@ -16,7 +16,7 @@ LL | Err(_) => panic!(), = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:26:9 + --> $DIR/match_wild_err_arm.rs:23:9 | LL | Err(_) => { | ^^^^^^ @@ -24,7 +24,7 @@ LL | Err(_) => { = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable error: `Err(_e)` matches all errors - --> $DIR/match_wild_err_arm.rs:34:9 + --> $DIR/match_wild_err_arm.rs:31:9 | LL | Err(_e) => panic!(), | ^^^^^^^ diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index b609ba659467f..ae237395b95fc 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -1,5 +1,7 @@ // run-rustfix -#![allow(unused_imports)] + +#![feature(custom_inner_attributes)] +#![allow(unused)] #![warn( clippy::all, clippy::style, @@ -77,3 +79,17 @@ fn main() { replace_with_default(); dont_lint_primitive(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let mut s = String::from("foo"); + let _ = std::mem::take(&mut s); +} diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 93f6dcdec83b9..3202e99e0be9e 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -1,5 +1,7 @@ // run-rustfix -#![allow(unused_imports)] + +#![feature(custom_inner_attributes)] +#![allow(unused)] #![warn( clippy::all, clippy::style, @@ -77,3 +79,17 @@ fn main() { replace_with_default(); dont_lint_primitive(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index 90dc6c95f8582..dd8a50dab9002 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:15:13 + --> $DIR/mem_replace.rs:17: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:17:13 + --> $DIR/mem_replace.rs:19: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:22:13 + --> $DIR/mem_replace.rs:24:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -21,100 +21,106 @@ 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:25:13 + --> $DIR/mem_replace.rs:27: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:26:13 + --> $DIR/mem_replace.rs:28:13 | LL | let _ = std::mem::replace(s, Default::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:29:13 + --> $DIR/mem_replace.rs:31: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 + --> $DIR/mem_replace.rs:32: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 + --> $DIR/mem_replace.rs:33: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 + --> $DIR/mem_replace.rs:34: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 + --> $DIR/mem_replace.rs:37: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 + --> $DIR/mem_replace.rs:40: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 + --> $DIR/mem_replace.rs:43: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 + --> $DIR/mem_replace.rs:46: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 + --> $DIR/mem_replace.rs:49: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 + --> $DIR/mem_replace.rs:52: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 + --> $DIR/mem_replace.rs:55:13 | LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:56:13 + --> $DIR/mem_replace.rs:58:13 | LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:59:13 + --> $DIR/mem_replace.rs:61:13 | LL | let _ = std::mem::replace(&mut refstr, ""); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:62:13 + --> $DIR/mem_replace.rs:64:13 | LL | let _ = std::mem::replace(&mut slice, &[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` -error: aborting due to 19 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:94:13 + | +LL | let _ = std::mem::replace(&mut s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` + +error: aborting due to 20 previous errors diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index c4c6391bb4c1d..cd148063bf065 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -1,240 +1,29 @@ #![allow(clippy::redundant_clone)] #![feature(custom_inner_attributes)] -#![clippy::msrv = "1.0.0"] -use std::ops::{Deref, RangeFrom}; +fn main() {} -fn approx_const() { +fn just_under_msrv() { + #![clippy::msrv = "1.42.0"] let log2_10 = 3.321928094887362; - let log10_2 = 0.301029995663981; } -fn cloned_instead_of_copied() { - let _ = [1].iter().cloned(); -} - -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 - }; -} - -pub fn manual_strip_msrv() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } -} - -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 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"]; - - #[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(); -} - -#[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; -} - -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(&mut 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); -} - -// Could be const -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) {} -} - -#[cfg_attr(rustfmt, rustfmt_skip)] -fn deprecated_cfg_attr() {} - -#[warn(clippy::cast_lossless)] -fn int_from_bool() -> u8 { - true as u8 -} - -fn err_expect() { - let x: Result = Ok(10); - x.err().expect("Testing expect_err"); -} - -fn cast_abs_to_unsigned() { - let x: i32 = 10; - assert_eq!(10u32, x.abs() as u32); -} - -fn manual_rem_euclid() { - let x: i32 = 10; - let _: i32 = ((x % 4) + 4) % 4; -} - -fn manual_clamp() { - let (input, min, max) = (0, -1, 2); - let _ = if input < min { - min - } else if input > max { - max - } else { - input - }; -} - -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(); - missing_const_for_fn(); - unnest_or_patterns(); - int_from_bool(); - err_expect(); - cast_abs_to_unsigned(); - manual_rem_euclid(); - manual_clamp(); +fn meets_msrv() { + #![clippy::msrv = "1.43.0"] + let log2_10 = 3.321928094887362; } -mod just_under_msrv { - #![feature(custom_inner_attributes)] +fn just_above_msrv() { #![clippy::msrv = "1.44.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } -} - -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!"); - } - } + let log2_10 = 3.321928094887362; } -mod just_above_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!"); - } - } +fn no_patch_under() { + #![clippy::msrv = "1.42"] + let log2_10 = 3.321928094887362; } -mod const_rem_euclid { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.50.0"] - - pub const fn const_rem_euclid_4(num: i32) -> i32 { - ((num % 4) + 4) % 4 - } +fn no_patch_meets() { + #![clippy::msrv = "1.43"] + let log2_10 = 3.321928094887362; } diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index d1cffc26a8318..68aa58748190b 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,37 +1,27 @@ -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:216:24 +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:13:19 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:215:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: `-D clippy::manual-strip` implied by `-D warnings` -help: try using the `strip_prefix` method - | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | + = help: consider using the constant directly + = note: `#[deny(clippy::approx_constant)]` on by default -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:228:24 +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:18:19 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:227:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using the `strip_prefix` method + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:28:19 | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | + = help: consider using the constant directly -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs index f20841891a742..02892f329af67 100644 --- a/tests/ui/min_rust_version_invalid_attr.rs +++ b/tests/ui/min_rust_version_invalid_attr.rs @@ -2,3 +2,17 @@ #![clippy::msrv = "invalid.version"] fn main() {} + +#[clippy::msrv = "invalid.version"] +fn outer_attr() {} + +mod multiple { + #![clippy::msrv = "1.40"] + #![clippy::msrv = "=1.35.0"] + #![clippy::msrv = "1.10.1"] + + mod foo { + #![clippy::msrv = "1"] + #![clippy::msrv = "1.0.0"] + } +} diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr index 6ff88ca56f8b9..93370a0fa9c91 100644 --- a/tests/ui/min_rust_version_invalid_attr.stderr +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -4,5 +4,47 @@ error: `invalid.version` is not a valid Rust version LL | #![clippy::msrv = "invalid.version"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: `msrv` cannot be an outer attribute + --> $DIR/min_rust_version_invalid_attr.rs:6:1 + | +LL | #[clippy::msrv = "invalid.version"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:11:5 + | +LL | #![clippy::msrv = "=1.35.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:10:5 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:12:5 + | +LL | #![clippy::msrv = "1.10.1"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:10:5 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:16:9 + | +LL | #![clippy::msrv = "1.0.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:15:9 + | +LL | #![clippy::msrv = "1"] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/min_rust_version_multiple_inner_attr.rs b/tests/ui/min_rust_version_multiple_inner_attr.rs deleted file mode 100644 index e882d5ccf91a8..0000000000000 --- a/tests/ui/min_rust_version_multiple_inner_attr.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![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 deleted file mode 100644 index e3ff6605cde87..0000000000000 --- a/tests/ui/min_rust_version_multiple_inner_attr.stderr +++ /dev/null @@ -1,38 +0,0 @@ -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 - diff --git a/tests/ui/min_rust_version_no_patch.rs b/tests/ui/min_rust_version_no_patch.rs deleted file mode 100644 index 98fffe1e3512b..0000000000000 --- a/tests/ui/min_rust_version_no_patch.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![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 deleted file mode 100644 index 551948bd72ef1..0000000000000 --- a/tests/ui/min_rust_version_outer_attr.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![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 deleted file mode 100644 index 579ee7a87d23c..0000000000000 --- a/tests/ui/min_rust_version_outer_attr.stderr +++ /dev/null @@ -1,8 +0,0 @@ -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 - 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 88f6935d224ae..b85e88784918d 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -77,5 +77,17 @@ mod const_fn_stabilized_before_msrv { } } +fn msrv_1_45() -> i32 { + #![clippy::msrv = "1.45"] + + 45 +} + +fn msrv_1_46() -> i32 { + #![clippy::msrv = "1.46"] + + 46 +} + // 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 3eb52b6827475..f8e221c82f1a5 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -81,5 +81,15 @@ LL | | byte.is_ascii_digit(); LL | | } | |_____^ -error: aborting due to 10 previous errors +error: this could be a `const fn` + --> $DIR/could_be_const.rs:86:1 + | +LL | / fn msrv_1_46() -> i32 { +LL | | #![clippy::msrv = "1.46"] +LL | | +LL | | 46 +LL | | } + | |_^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/missing_trait_methods.rs b/tests/ui/missing_trait_methods.rs new file mode 100644 index 0000000000000..8df885919a3e6 --- /dev/null +++ b/tests/ui/missing_trait_methods.rs @@ -0,0 +1,50 @@ +#![allow(unused, clippy::needless_lifetimes)] +#![warn(clippy::missing_trait_methods)] + +trait A { + fn provided() {} +} + +trait B { + fn required(); + + fn a(_: usize) -> usize { + 1 + } + + fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] { + a.as_ref() + } +} + +struct Partial; + +impl A for Partial {} + +impl B for Partial { + fn required() {} + + fn a(_: usize) -> usize { + 2 + } +} + +struct Complete; + +impl A for Complete { + fn provided() {} +} + +impl B for Complete { + fn required() {} + + fn a(_: usize) -> usize { + 2 + } + + fn b>(a: &T) -> &[u8] { + a.as_ref() + } +} + +fn main() {} diff --git a/tests/ui/missing_trait_methods.stderr b/tests/ui/missing_trait_methods.stderr new file mode 100644 index 0000000000000..0c5205e196572 --- /dev/null +++ b/tests/ui/missing_trait_methods.stderr @@ -0,0 +1,27 @@ +error: missing trait method provided by default: `provided` + --> $DIR/missing_trait_methods.rs:22:1 + | +LL | impl A for Partial {} + | ^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> $DIR/missing_trait_methods.rs:5:5 + | +LL | fn provided() {} + | ^^^^^^^^^^^^^ + = note: `-D clippy::missing-trait-methods` implied by `-D warnings` + +error: missing trait method provided by default: `b` + --> $DIR/missing_trait_methods.rs:24:1 + | +LL | impl B for Partial { + | ^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> $DIR/missing_trait_methods.rs:15:5 + | +LL | fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index aa2687159ef47..340e89d2db1d2 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -3,7 +3,11 @@ #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables)] -#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] +#[allow( + clippy::uninlined_format_args, + clippy::unnecessary_mut_passed, + clippy::unnecessary_to_owned +)] fn main() { let a = 5; let ref_a = &a; @@ -134,6 +138,7 @@ fn main() { multiple_constraints([[""]]); multiple_constraints_normalizes_to_same(X, X); let _ = Some("").unwrap_or(""); + let _ = std::fs::write("x", "".to_string()); only_sized(&""); // Don't lint. `Sized` is only bound let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound @@ -276,8 +281,9 @@ mod copyable_iterator { fn dont_warn(mut x: Iter) { takes_iter(&mut x); } + #[allow(unused_mut)] fn warn(mut x: &mut Iter) { - takes_iter(&mut x) + takes_iter(x) } } @@ -327,3 +333,55 @@ fn issue9383() { ManuallyDrop::drop(&mut ocean.coral); } } + +#[allow(dead_code)] +fn closure_test() { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", arg); + let _ = std::fs::write("x", loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); +} + +#[allow(dead_code)] +mod significant_drop { + #[derive(Debug)] + struct X; + + #[derive(Debug)] + struct Y; + + impl Drop for Y { + fn drop(&mut self) {} + } + + fn foo(x: X, y: Y) { + debug(x); + debug(&y); // Don't lint. Has significant drop + } + + fn debug(_: impl std::fmt::Debug) {} +} + +#[allow(dead_code)] +mod used_exactly_once { + fn foo(x: String) { + use_x(x); + } + fn use_x(_: impl AsRef) {} +} + +#[allow(dead_code)] +mod used_more_than_once { + fn foo(x: String) { + use_x(&x); + use_x_again(&x); + } + fn use_x(_: impl AsRef) {} + fn use_x_again(_: impl AsRef) {} +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index d41251e8f6aac..c93711ac8e284 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -3,7 +3,11 @@ #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables)] -#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] +#[allow( + clippy::uninlined_format_args, + clippy::unnecessary_mut_passed, + clippy::unnecessary_to_owned +)] fn main() { let a = 5; let ref_a = &a; @@ -134,6 +138,7 @@ fn main() { multiple_constraints(&[[""]]); multiple_constraints_normalizes_to_same(&X, X); let _ = Some("").unwrap_or(&""); + let _ = std::fs::write("x", &"".to_string()); only_sized(&""); // Don't lint. `Sized` is only bound let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound @@ -276,6 +281,7 @@ mod copyable_iterator { fn dont_warn(mut x: Iter) { takes_iter(&mut x); } + #[allow(unused_mut)] fn warn(mut x: &mut Iter) { takes_iter(&mut x) } @@ -327,3 +333,55 @@ fn issue9383() { ManuallyDrop::drop(&mut ocean.coral); } } + +#[allow(dead_code)] +fn closure_test() { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", &arg); + let _ = std::fs::write("x", &loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); +} + +#[allow(dead_code)] +mod significant_drop { + #[derive(Debug)] + struct X; + + #[derive(Debug)] + struct Y; + + impl Drop for Y { + fn drop(&mut self) {} + } + + fn foo(x: X, y: Y) { + debug(&x); + debug(&y); // Don't lint. Has significant drop + } + + fn debug(_: impl std::fmt::Debug) {} +} + +#[allow(dead_code)] +mod used_exactly_once { + fn foo(x: String) { + use_x(&x); + } + fn use_x(_: impl AsRef) {} +} + +#[allow(dead_code)] +mod used_more_than_once { + fn foo(x: String) { + use_x(&x); + use_x_again(&x); + } + fn use_x(_: impl AsRef) {} + fn use_x_again(_: impl AsRef) {} +} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 5af68706d4ba5..8b593268bec21 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -1,5 +1,5 @@ error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:11:15 + --> $DIR/needless_borrow.rs:15:15 | LL | let _ = x(&&a); // warn | ^^^ help: change this to: `&a` @@ -7,172 +7,208 @@ LL | let _ = x(&&a); // warn = note: `-D clippy::needless-borrow` implied by `-D warnings` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:15:13 + --> $DIR/needless_borrow.rs:19:13 | LL | mut_ref(&mut &mut b); // warn | ^^^^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:27:13 + --> $DIR/needless_borrow.rs:31:13 | LL | &&a | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:29:15 + --> $DIR/needless_borrow.rs:33:15 | LL | 46 => &&a, | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:35:27 + --> $DIR/needless_borrow.rs:39:27 | LL | break &ref_a; | ^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:42:15 + --> $DIR/needless_borrow.rs:46:15 | LL | let _ = x(&&&a); | ^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:43:15 + --> $DIR/needless_borrow.rs:47:15 | LL | let _ = x(&mut &&a); | ^^^^^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:44:15 + --> $DIR/needless_borrow.rs:48:15 | LL | let _ = x(&&&mut b); | ^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:45:15 + --> $DIR/needless_borrow.rs:49:15 | LL | let _ = x(&&ref_a); | ^^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:48:11 + --> $DIR/needless_borrow.rs:52:11 | LL | x(&b); | ^^ help: change this to: `b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:55:13 + --> $DIR/needless_borrow.rs:59:13 | LL | mut_ref(&mut x); | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:56:13 + --> $DIR/needless_borrow.rs:60:13 | LL | mut_ref(&mut &mut x); | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:57:23 + --> $DIR/needless_borrow.rs:61:23 | LL | let y: &mut i32 = &mut x; | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:58:23 + --> $DIR/needless_borrow.rs:62:23 | LL | let y: &mut i32 = &mut &mut x; | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:67:14 + --> $DIR/needless_borrow.rs:71:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:73:14 + --> $DIR/needless_borrow.rs:77:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:85:13 + --> $DIR/needless_borrow.rs:89:13 | LL | let _ = (&x).0; | ^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:87:22 + --> $DIR/needless_borrow.rs:91:22 | LL | let _ = unsafe { (&*x).0 }; | ^^^^^ help: change this to: `(*x)` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:97:5 + --> $DIR/needless_borrow.rs:101:5 | LL | (&&()).foo(); | ^^^^^^ help: change this to: `(&())` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:106:5 + --> $DIR/needless_borrow.rs:110:5 | LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:131:51 + --> $DIR/needless_borrow.rs:135:51 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:132:44 + --> $DIR/needless_borrow.rs:136:44 | LL | let _ = std::path::Path::new(".").join(&&"."); | ^^^^^ help: change this to: `"."` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:133:23 + --> $DIR/needless_borrow.rs:137:23 | LL | deref_target_is_x(&X); | ^^ help: change this to: `X` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:134:26 + --> $DIR/needless_borrow.rs:138:26 | LL | multiple_constraints(&[[""]]); | ^^^^^^^ help: change this to: `[[""]]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:135:45 + --> $DIR/needless_borrow.rs:139:45 | LL | multiple_constraints_normalizes_to_same(&X, X); | ^^ help: change this to: `X` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:136:32 + --> $DIR/needless_borrow.rs:140:32 | LL | let _ = Some("").unwrap_or(&""); | ^^^ help: change this to: `""` +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:141:33 + | +LL | let _ = std::fs::write("x", &"".to_string()); + | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` + error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:187:13 + --> $DIR/needless_borrow.rs:192:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:196:13 + --> $DIR/needless_borrow.rs:201:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:298:55 + --> $DIR/needless_borrow.rs:286:20 + | +LL | takes_iter(&mut x) + | ^^^^^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:304:55 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` -error: aborting due to 29 previous errors +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:344:37 + | +LL | let _ = std::fs::write("x", &arg); + | ^^^^ help: change this to: `arg` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:345:37 + | +LL | let _ = std::fs::write("x", &loc); + | ^^^^ help: change this to: `loc` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:364:15 + | +LL | debug(&x); + | ^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:374:15 + | +LL | use_x(&x); + | ^^ help: change this to: `x` + +error: aborting due to 35 previous errors diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed index 07d7f0b45b0c2..bc376d0d7fb39 100644 --- a/tests/ui/option_as_ref_deref.fixed +++ b/tests/ui/option_as_ref_deref.fixed @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports, clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] use std::ffi::{CString, OsString}; @@ -42,3 +43,17 @@ fn main() { // Issue #5927 let _ = opt.as_deref(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let opt = Some(String::from("123")); + let _ = opt.as_deref(); +} diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs index 6ae059c9425d3..ba3a2eedc225c 100644 --- a/tests/ui/option_as_ref_deref.rs +++ b/tests/ui/option_as_ref_deref.rs @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports, clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] use std::ffi::{CString, OsString}; @@ -45,3 +46,17 @@ fn main() { // Issue #5927 let _ = opt.as_ref().map(std::ops::Deref::deref); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index 62f2823247528..7de8b3b6ba435 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -1,5 +1,5 @@ error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:13:13 + --> $DIR/option_as_ref_deref.rs:14:13 | LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()` @@ -7,7 +7,7 @@ LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); = note: `-D clippy::option-as-ref-deref` implied by `-D warnings` error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:16:13 + --> $DIR/option_as_ref_deref.rs:17:13 | LL | let _ = opt.clone() | _____________^ @@ -17,94 +17,100 @@ LL | | ) | |_________^ help: try using as_deref instead: `opt.clone().as_deref()` error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:22:13 + --> $DIR/option_as_ref_deref.rs:23:13 | LL | let _ = opt.as_mut().map(DerefMut::deref_mut); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:24:13 + --> $DIR/option_as_ref_deref.rs:25:13 | LL | let _ = opt.as_ref().map(String::as_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:25:13 + --> $DIR/option_as_ref_deref.rs:26:13 | LL | let _ = opt.as_ref().map(|x| x.as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:26:13 + --> $DIR/option_as_ref_deref.rs:27:13 | LL | let _ = opt.as_mut().map(String::as_mut_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:27:13 + --> $DIR/option_as_ref_deref.rs:28:13 | LL | let _ = opt.as_mut().map(|x| x.as_mut_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:28:13 + --> $DIR/option_as_ref_deref.rs:29:13 | LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()` error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:29:13 + --> $DIR/option_as_ref_deref.rs:30:13 | LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()` error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:30:13 + --> $DIR/option_as_ref_deref.rs:31:13 | LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()` error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:31:13 + --> $DIR/option_as_ref_deref.rs:32:13 | LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()` error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:32:13 + --> $DIR/option_as_ref_deref.rs:33:13 | LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()` error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:34:13 + --> $DIR/option_as_ref_deref.rs:35:13 | LL | let _ = opt.as_ref().map(|x| x.deref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:35:13 + --> $DIR/option_as_ref_deref.rs:36:13 | LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()` error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:42:13 + --> $DIR/option_as_ref_deref.rs:43:13 | LL | let _ = opt.as_ref().map(|x| &**x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:43:13 + --> $DIR/option_as_ref_deref.rs:44:13 | LL | let _ = opt.as_mut().map(|x| &mut **x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` 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 + --> $DIR/option_as_ref_deref.rs:47: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 +error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:61:13 + | +LL | let _ = opt.as_ref().map(String::as_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: aborting due to 18 previous errors diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 896430780ea80..23b1aa8bebd53 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -225,4 +225,15 @@ mod issue8239 { } } +mod issue9608 { + fn sig_drop() { + enum X { + X(std::fs::File), + Y(u32), + } + + let _ = None.unwrap_or(X::Y(0)); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 2473163d4fd2f..039998f22dd71 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -225,4 +225,15 @@ mod issue8239 { } } +mod issue9608 { + fn sig_drop() { + enum X { + X(std::fs::File), + Y(u32), + } + + let _ = None.unwrap_or(X::Y(0)); + } +} + fn main() {} diff --git a/tests/ui/partial_pub_fields.rs b/tests/ui/partial_pub_fields.rs new file mode 100644 index 0000000000000..668545da84419 --- /dev/null +++ b/tests/ui/partial_pub_fields.rs @@ -0,0 +1,40 @@ +#![allow(unused)] +#![warn(clippy::partial_pub_fields)] + +fn main() { + use std::collections::HashMap; + + #[derive(Default)] + pub struct FileSet { + files: HashMap, + pub paths: HashMap, + } + + pub struct Color { + pub r: u8, + pub g: u8, + b: u8, + } + + pub struct Point(i32, pub i32); + + pub struct Visibility { + r#pub: bool, + pub pos: u32, + } + + // Don't lint on empty structs; + pub struct Empty1; + pub struct Empty2(); + pub struct Empty3 {}; + + // Don't lint on structs with one field. + pub struct Single1(i32); + pub struct Single2(pub i32); + pub struct Single3 { + v1: i32, + } + pub struct Single4 { + pub v1: i32, + } +} diff --git a/tests/ui/partial_pub_fields.stderr b/tests/ui/partial_pub_fields.stderr new file mode 100644 index 0000000000000..84cfc1a91940d --- /dev/null +++ b/tests/ui/partial_pub_fields.stderr @@ -0,0 +1,35 @@ +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:10:9 + | +LL | pub paths: HashMap, + | ^^^ + | + = help: consider using private field here + = note: `-D clippy::partial-pub-fields` implied by `-D warnings` + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:16:9 + | +LL | b: u8, + | ^ + | + = help: consider using public field here + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:19:27 + | +LL | pub struct Point(i32, pub i32); + | ^^^ + | + = help: consider using private field here + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:23:9 + | +LL | pub pos: u32, + | ^^^ + | + = help: consider using private field here + +error: aborting due to 4 previous errors + diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index fd15001e540c6..5f54101ca15ad 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -3,7 +3,7 @@ #![warn(clippy::ptr_arg)] use std::borrow::Cow; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; fn do_vec(x: &Vec) { //Nothing here @@ -207,3 +207,31 @@ fn cow_conditional_to_mut(a: &mut Cow) { a.to_mut().push_str("foo"); } } + +// Issue #9542 +fn dyn_trait_ok(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + trait T {} + impl T for Vec {} + impl T for String {} + impl T for PathBuf {} + fn takes_dyn(_: &mut dyn T) {} + + takes_dyn(a); + takes_dyn(b); + takes_dyn(c); +} + +fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + trait T {} + impl T for Vec {} + impl T for [U] {} + impl T for String {} + impl T for str {} + impl T for PathBuf {} + impl T for Path {} + fn takes_dyn(_: &mut dyn T) {} + + takes_dyn(a); + takes_dyn(b); + takes_dyn(c); +} diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index d64b5f454a5aa..6b4de98ce88c6 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -162,5 +162,23 @@ error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a sl LL | fn mut_vec_slice_methods(v: &mut Vec) { | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` -error: aborting due to 17 previous errors +error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:17 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` + +error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:35 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^ help: change this to: `&mut str` + +error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:51 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^^ help: change this to: `&mut Path` + +error: aborting due to 20 previous errors diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 85d021b2f25e2..824f00cb99e85 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -1,10 +1,12 @@ // run-rustfix -#[warn(clippy::manual_range_contains)] -#[allow(unused)] -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] +#![feature(custom_inner_attributes)] +#![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_i32; @@ -62,3 +64,17 @@ fn main() { pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let x = 5; + x >= 8 && x < 34; +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let x = 5; + (8..35).contains(&x); +} diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 9a7a75dc13254..df925eeadfe5e 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -1,10 +1,12 @@ // run-rustfix -#[warn(clippy::manual_range_contains)] -#[allow(unused)] -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] +#![feature(custom_inner_attributes)] +#![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_i32; @@ -62,3 +64,17 @@ fn main() { pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let x = 5; + x >= 8 && x < 34; +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let x = 5; + x >= 8 && x < 35; +} diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 936859db5a126..9689e665b05c5 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -1,5 +1,5 @@ error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:12:5 + --> $DIR/range_contains.rs:14:5 | LL | x >= 8 && x < 12; | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)` @@ -7,118 +7,124 @@ LL | x >= 8 && x < 12; = note: `-D clippy::manual-range-contains` implied by `-D warnings` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:13:5 + --> $DIR/range_contains.rs:15:5 | LL | x < 42 && x >= 21; | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:14:5 + --> $DIR/range_contains.rs:16:5 | LL | 100 > x && 1 <= x; | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:17:5 + --> $DIR/range_contains.rs:19:5 | LL | x >= 9 && x <= 99; | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:18:5 + --> $DIR/range_contains.rs:20:5 | LL | x <= 33 && x >= 1; | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:19:5 + --> $DIR/range_contains.rs:21:5 | LL | 999 >= x && 1 <= x; | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:22:5 + --> $DIR/range_contains.rs:24:5 | LL | x < 8 || x >= 12; | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:23:5 + --> $DIR/range_contains.rs:25:5 | LL | x >= 42 || x < 21; | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:24:5 + --> $DIR/range_contains.rs:26:5 | LL | 100 <= x || 1 > x; | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:27:5 + --> $DIR/range_contains.rs:29:5 | LL | x < 9 || x > 99; | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:28:5 + --> $DIR/range_contains.rs:30:5 | LL | x > 33 || x < 1; | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:29:5 + --> $DIR/range_contains.rs:31:5 | LL | 999 < x || 1 > x; | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:44:5 + --> $DIR/range_contains.rs:46:5 | LL | y >= 0. && y < 1.; | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:45:5 + --> $DIR/range_contains.rs:47:5 | LL | y < 0. || y > 1.; | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:48:5 + --> $DIR/range_contains.rs:50:5 | LL | x >= -10 && x <= 10; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:50:5 + --> $DIR/range_contains.rs:52:5 | LL | y >= -3. && y <= 3.; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:55:30 + --> $DIR/range_contains.rs:57:30 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:55:5 + --> $DIR/range_contains.rs:57:5 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:56:29 + --> $DIR/range_contains.rs:58:29 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:56:5 + --> $DIR/range_contains.rs:58:5 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)` -error: aborting due to 20 previous errors +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:79:5 + | +LL | x >= 8 && x < 35; + | ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)` + +error: aborting due to 21 previous errors diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index 5b4b8eeedd469..34ab552cb1d8d 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,4 +1,6 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -69,3 +71,17 @@ fn issue_3476() { S { foo: foo:: }; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let start = 0; + let _ = RangeFrom { start: start }; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let start = 0; + let _ = RangeFrom { start }; +} diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index 3f97b80c56828..a051b1f96f0fd 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,4 +1,6 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -69,3 +71,17 @@ fn issue_3476() { S { foo: foo:: }; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let start = 0; + let _ = RangeFrom { start: start }; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let start = 0; + let _ = RangeFrom { start: start }; +} diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr index 7976292df2241..8b82e062b93a6 100644 --- a/tests/ui/redundant_field_names.stderr +++ b/tests/ui/redundant_field_names.stderr @@ -1,5 +1,5 @@ error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:34:9 + --> $DIR/redundant_field_names.rs:36:9 | LL | gender: gender, | ^^^^^^^^^^^^^^ help: replace it with: `gender` @@ -7,40 +7,46 @@ LL | gender: gender, = note: `-D clippy::redundant-field-names` implied by `-D warnings` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:35:9 + --> $DIR/redundant_field_names.rs:37:9 | LL | age: age, | ^^^^^^^^ help: replace it with: `age` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:56:25 + --> $DIR/redundant_field_names.rs:58:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:57:23 + --> $DIR/redundant_field_names.rs:59:23 | LL | let _ = RangeTo { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:21 + --> $DIR/redundant_field_names.rs:60:21 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:35 + --> $DIR/redundant_field_names.rs:60:35 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:60:32 + --> $DIR/redundant_field_names.rs:62:32 | LL | let _ = RangeToInclusive { end: end }; | ^^^^^^^^ help: replace it with: `end` -error: aborting due to 7 previous errors +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:86:25 + | +LL | let _ = RangeFrom { start: start }; + | ^^^^^^^^^^^^ help: replace it with: `start` + +error: aborting due to 8 previous errors diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed index acc8f1e25b6ed..42110dbe81e84 100644 --- a/tests/ui/redundant_static_lifetimes.fixed +++ b/tests/ui/redundant_static_lifetimes.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -54,3 +55,15 @@ impl Foo { impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + static V: &'static u8 = &16; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + static V: &u8 = &17; +} diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs index f2f0f78659c93..bc5200bc8625b 100644 --- a/tests/ui/redundant_static_lifetimes.rs +++ b/tests/ui/redundant_static_lifetimes.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -54,3 +55,15 @@ impl Foo { impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + static V: &'static u8 = &16; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + static V: &'static u8 = &17; +} diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 649831f9c069a..735113460d28c 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -1,5 +1,5 @@ error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:8:17 + --> $DIR/redundant_static_lifetimes.rs:9:17 | LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` @@ -7,94 +7,100 @@ 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 - --> $DIR/redundant_static_lifetimes.rs:12:21 + --> $DIR/redundant_static_lifetimes.rs:13: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 - --> $DIR/redundant_static_lifetimes.rs:14:32 + --> $DIR/redundant_static_lifetimes.rs:15: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 - --> $DIR/redundant_static_lifetimes.rs:14:47 + --> $DIR/redundant_static_lifetimes.rs:15: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 - --> $DIR/redundant_static_lifetimes.rs:16:17 + --> $DIR/redundant_static_lifetimes.rs:17:17 | LL | const VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:18:20 + --> $DIR/redundant_static_lifetimes.rs:19:20 | LL | const VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:20:19 + --> $DIR/redundant_static_lifetimes.rs:21: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 - --> $DIR/redundant_static_lifetimes.rs:22:19 + --> $DIR/redundant_static_lifetimes.rs:23: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 - --> $DIR/redundant_static_lifetimes.rs:24:19 + --> $DIR/redundant_static_lifetimes.rs:25: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 - --> $DIR/redundant_static_lifetimes.rs:26:25 + --> $DIR/redundant_static_lifetimes.rs:27: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 - --> $DIR/redundant_static_lifetimes.rs:30:29 + --> $DIR/redundant_static_lifetimes.rs:31: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 - --> $DIR/redundant_static_lifetimes.rs:32:25 + --> $DIR/redundant_static_lifetimes.rs:33:25 | LL | static STATIC_VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:34:28 + --> $DIR/redundant_static_lifetimes.rs:35:28 | LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:36:27 + --> $DIR/redundant_static_lifetimes.rs:37: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 - --> $DIR/redundant_static_lifetimes.rs:38:27 + --> $DIR/redundant_static_lifetimes.rs:39: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 - --> $DIR/redundant_static_lifetimes.rs:40:27 + --> $DIR/redundant_static_lifetimes.rs:41:27 | LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` -error: aborting due to 16 previous errors +error: statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:68:16 + | +LL | static V: &'static u8 = &17; + | -^^^^^^^--- help: consider removing `'static`: `&u8` + +error: aborting due to 17 previous errors diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 2df45c927d716..e487799e15220 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -45,3 +45,8 @@ impl RefOptTrait for u32 { fn main() { let x: &Option<&u32> = &None; } + +fn issue9682(arg: &Option<&mut String>) { + // Should not lint, as the inner ref is mutable making it non `Copy` + println!("{arg:?}"); +} diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index 3ca7a40190253..106274479751d 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -150,6 +150,19 @@ fn tester(fn_arg: i32) { println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); println!("{}", with_span!(span val)); + + if local_i32 > 0 { + panic!("p1 {local_i32}"); + } + if local_i32 > 0 { + panic!("p2 {local_i32}"); + } + if local_i32 > 0 { + panic!("p3 {local_i32}"); + } + if local_i32 > 0 { + panic!("p4 {local_i32}"); + } } fn main() { diff --git a/tests/ui/uninlined_format_args.rs b/tests/ui/uninlined_format_args.rs index 924191f4324cf..8e495ebd083a5 100644 --- a/tests/ui/uninlined_format_args.rs +++ b/tests/ui/uninlined_format_args.rs @@ -150,6 +150,19 @@ fn tester(fn_arg: i32) { println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); println!("{}", with_span!(span val)); + + if local_i32 > 0 { + panic!("p1 {}", local_i32); + } + if local_i32 > 0 { + panic!("p2 {0}", local_i32); + } + if local_i32 > 0 { + panic!("p3 {local_i32}", local_i32 = local_i32); + } + if local_i32 > 0 { + panic!("p4 {local_i32}"); + } } fn main() { diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index d1a7749263429..2ce3b7fa960c6 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -828,7 +828,43 @@ LL + println!("{val}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:168:5 + --> $DIR/uninlined_format_args.rs:155:9 + | +LL | panic!("p1 {}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p1 {}", local_i32); +LL + panic!("p1 {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:158:9 + | +LL | panic!("p2 {0}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p2 {0}", local_i32); +LL + panic!("p2 {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:161:9 + | +LL | panic!("p3 {local_i32}", local_i32 = local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p3 {local_i32}", local_i32 = local_i32); +LL + panic!("p3 {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:181:5 | LL | println!("expand='{}'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -839,5 +875,5 @@ LL - println!("expand='{}'", local_i32); LL + println!("expand='{local_i32}'"); | -error: aborting due to 70 previous errors +error: aborting due to 73 previous errors diff --git a/tests/ui/uninlined_format_args_panic.edition2018.fixed b/tests/ui/uninlined_format_args_panic.edition2018.fixed new file mode 100644 index 0000000000000..96cc0877960ec --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.edition2018.fixed @@ -0,0 +1,29 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix + +#![warn(clippy::uninlined_format_args)] + +fn main() { + let var = 1; + + println!("val='{var}'"); + + if var > 0 { + panic!("p1 {}", var); + } + if var > 0 { + panic!("p2 {0}", var); + } + if var > 0 { + panic!("p3 {var}", var = var); + } + + #[allow(non_fmt_panics)] + { + if var > 0 { + panic!("p4 {var}"); + } + } +} diff --git a/tests/ui/uninlined_format_args_panic.edition2018.stderr b/tests/ui/uninlined_format_args_panic.edition2018.stderr new file mode 100644 index 0000000000000..2c8061259229a --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.edition2018.stderr @@ -0,0 +1,15 @@ +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:11:5 + | +LL | println!("val='{}'", var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninlined-format-args` implied by `-D warnings` +help: change this to + | +LL - println!("val='{}'", var); +LL + println!("val='{var}'"); + | + +error: aborting due to previous error + diff --git a/tests/ui/uninlined_format_args_panic.edition2021.fixed b/tests/ui/uninlined_format_args_panic.edition2021.fixed new file mode 100644 index 0000000000000..faf8ca4d3a797 --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.edition2021.fixed @@ -0,0 +1,29 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix + +#![warn(clippy::uninlined_format_args)] + +fn main() { + let var = 1; + + println!("val='{var}'"); + + if var > 0 { + panic!("p1 {var}"); + } + if var > 0 { + panic!("p2 {var}"); + } + if var > 0 { + panic!("p3 {var}"); + } + + #[allow(non_fmt_panics)] + { + if var > 0 { + panic!("p4 {var}"); + } + } +} diff --git a/tests/ui/uninlined_format_args_panic.edition2021.stderr b/tests/ui/uninlined_format_args_panic.edition2021.stderr new file mode 100644 index 0000000000000..0f09c45f41324 --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.edition2021.stderr @@ -0,0 +1,51 @@ +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:11:5 + | +LL | println!("val='{}'", var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninlined-format-args` implied by `-D warnings` +help: change this to + | +LL - println!("val='{}'", var); +LL + println!("val='{var}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:14:9 + | +LL | panic!("p1 {}", var); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p1 {}", var); +LL + panic!("p1 {var}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:17:9 + | +LL | panic!("p2 {0}", var); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p2 {0}", var); +LL + panic!("p2 {var}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:20:9 + | +LL | panic!("p3 {var}", var = var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p3 {var}", var = var); +LL + panic!("p3 {var}"); + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/uninlined_format_args_panic.rs b/tests/ui/uninlined_format_args_panic.rs new file mode 100644 index 0000000000000..6421c5bbed2f5 --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.rs @@ -0,0 +1,29 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix + +#![warn(clippy::uninlined_format_args)] + +fn main() { + let var = 1; + + println!("val='{}'", var); + + if var > 0 { + panic!("p1 {}", var); + } + if var > 0 { + panic!("p2 {0}", var); + } + if var > 0 { + panic!("p3 {var}", var = var); + } + + #[allow(non_fmt_panics)] + { + if var > 0 { + panic!("p4 {var}"); + } + } +} diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 94dc96427263c..ec8c6abfab91e 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -111,4 +111,8 @@ mod fixable { let _num = foo(); } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index e5150256f69ac..5213cdc269bd4 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -111,4 +111,8 @@ mod fixable { let _num = foo() as f32; } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index f97583aa22f9a..fe09aad06bc84 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] #![feature(custom_inner_attributes)] diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index aa5394a565790..3de6d0903c0f1 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] #![feature(custom_inner_attributes)] diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index c223b5bc711b2..9786c7b12128b 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -1,9 +1,9 @@ // run-rustfix -#![feature(box_patterns)] +#![feature(box_patterns, custom_inner_attributes)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] -#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused)] fn main() { // Should be ignored by this lint, as nesting requires more characters. @@ -33,3 +33,15 @@ fn main() { if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } + +fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + if let [1] | [52] = [0] {} +} + +fn msrv_1_53() { + #![clippy::msrv = "1.53"] + + if let [1 | 53] = [0] {} +} diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 04cd11036e4e0..f57322396d4ac 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -1,9 +1,9 @@ // run-rustfix -#![feature(box_patterns)] +#![feature(box_patterns, custom_inner_attributes)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] -#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused)] fn main() { // Should be ignored by this lint, as nesting requires more characters. @@ -33,3 +33,15 @@ fn main() { if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } + +fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + if let [1] | [52] = [0] {} +} + +fn msrv_1_53() { + #![clippy::msrv = "1.53"] + + if let [1] | [53] = [0] {} +} diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index 453c66cbba8fc..fbc12fff0b0e7 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -175,5 +175,16 @@ help: nest the patterns LL | if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} | ~~~~~~~~~~~~~~~~~ -error: aborting due to 16 previous errors +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:46:12 + | +LL | if let [1] | [53] = [0] {} + | ^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let [1 | 53] = [0] {} + | ~~~~~~~~ + +error: aborting due to 17 previous errors diff --git a/tests/ui/unused_format_specs.fixed b/tests/ui/unused_format_specs.fixed new file mode 100644 index 0000000000000..2930722b42d9d --- /dev/null +++ b/tests/ui/unused_format_specs.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +fn main() { + let f = 1.0f64; + println!("{}", 1.0); + println!("{f} {f:?}"); + + println!("{}", 1); +} + +fn should_not_lint() { + let f = 1.0f64; + println!("{:.1}", 1.0); + println!("{f:.w$} {f:.*?}", 3, w = 2); +} diff --git a/tests/ui/unused_format_specs.rs b/tests/ui/unused_format_specs.rs new file mode 100644 index 0000000000000..ee192a000d4b5 --- /dev/null +++ b/tests/ui/unused_format_specs.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +fn main() { + let f = 1.0f64; + println!("{:.}", 1.0); + println!("{f:.} {f:.?}"); + + println!("{:.}", 1); +} + +fn should_not_lint() { + let f = 1.0f64; + println!("{:.1}", 1.0); + println!("{f:.w$} {f:.*?}", 3, w = 2); +} diff --git a/tests/ui/unused_format_specs.stderr b/tests/ui/unused_format_specs.stderr new file mode 100644 index 0000000000000..7231c17e74c19 --- /dev/null +++ b/tests/ui/unused_format_specs.stderr @@ -0,0 +1,54 @@ +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:8:17 + | +LL | println!("{:.}", 1.0); + | ^ + | + = note: a precision specifier is not required to format floats + = note: `-D clippy::unused-format-specs` implied by `-D warnings` +help: remove the `.` + | +LL - println!("{:.}", 1.0); +LL + println!("{}", 1.0); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:9:18 + | +LL | println!("{f:.} {f:.?}"); + | ^ + | + = note: a precision specifier is not required to format floats +help: remove the `.` + | +LL - println!("{f:.} {f:.?}"); +LL + println!("{f} {f:.?}"); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:9:24 + | +LL | println!("{f:.} {f:.?}"); + | ^ + | + = note: a precision specifier is not required to format floats +help: remove the `.` + | +LL - println!("{f:.} {f:.?}"); +LL + println!("{f:.} {f:?}"); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:11:17 + | +LL | println!("{:.}", 1); + | ^ + | +help: remove the `.` + | +LL - println!("{:.}", 1); +LL + println!("{}", 1); + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/unused_format_specs_unfixable.rs b/tests/ui/unused_format_specs_unfixable.rs new file mode 100644 index 0000000000000..78601a3483d34 --- /dev/null +++ b/tests/ui/unused_format_specs_unfixable.rs @@ -0,0 +1,30 @@ +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +macro_rules! format_args_from_macro { + () => { + format_args!("from macro") + }; +} + +fn main() { + // prints `.`, not ` .` + println!("{:5}.", format_args!("")); + //prints `abcde`, not `abc` + println!("{:.3}", format_args!("abcde")); + + println!("{:5}.", format_args_from_macro!()); + + let args = format_args!(""); + println!("{args:5}"); +} + +fn should_not_lint() { + println!("{}", format_args!("")); + // Technically the same as `{}`, but the `format_args` docs specifically mention that you can use + // debug formatting so allow it + println!("{:?}", format_args!("")); + + let args = format_args!(""); + println!("{args}"); +} diff --git a/tests/ui/unused_format_specs_unfixable.stderr b/tests/ui/unused_format_specs_unfixable.stderr new file mode 100644 index 0000000000000..9f1890282e6ac --- /dev/null +++ b/tests/ui/unused_format_specs_unfixable.stderr @@ -0,0 +1,69 @@ +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:12:15 + | +LL | println!("{:5}.", format_args!("")); + | ^^^^ + | + = note: `-D clippy::unused-format-specs` implied by `-D warnings` +help: for the width to apply consider using `format!()` + | +LL | println!("{:5}.", format!("")); + | ~~~~~~ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:5}.", format_args!("")); +LL + println!("{}.", format_args!("")); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:14:15 + | +LL | println!("{:.3}", format_args!("abcde")); + | ^^^^^ + | +help: for the precision to apply consider using `format!()` + | +LL | println!("{:.3}", format!("abcde")); + | ~~~~~~ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:.3}", format_args!("abcde")); +LL + println!("{}", format_args!("abcde")); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:16:15 + | +LL | println!("{:5}.", format_args_from_macro!()); + | ^^^^ + | +help: for the width to apply consider using `format!()` + --> $DIR/unused_format_specs_unfixable.rs:16:17 + | +LL | println!("{:5}.", format_args_from_macro!()); + | ^ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:5}.", format_args_from_macro!()); +LL + println!("{}.", format_args_from_macro!()); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:19:15 + | +LL | println!("{args:5}"); + | ^^^^^^^^ + | +help: for the width to apply consider using `format!()` + --> $DIR/unused_format_specs_unfixable.rs:19:21 + | +LL | println!("{args:5}"); + | ^ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{args:5}"); +LL + println!("{args}"); + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 37986187da179..3b54fe9d5ff3d 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -617,3 +618,35 @@ mod issue6902 { Bar = 1, } } + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} + +fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + Self::A => {}, + } + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 1b2b3337c92ed..bf87633cd2d8f 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -617,3 +618,35 @@ mod issue6902 { Bar = 1, } } + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} + +fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index f06bb959b3bde..16fb0609242cb 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:22:21 + --> $DIR/use_self.rs:23:21 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -7,244 +7,250 @@ LL | fn new() -> Foo { = note: `-D clippy::use-self` implied by `-D warnings` error: unnecessary structure name repetition - --> $DIR/use_self.rs:23:13 + --> $DIR/use_self.rs:24:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:25:22 + --> $DIR/use_self.rs:26:22 | LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:26:13 + --> $DIR/use_self.rs:27:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:31:25 + --> $DIR/use_self.rs:32:25 | LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:32:13 + --> $DIR/use_self.rs:33:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:97:24 + --> $DIR/use_self.rs:98:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:97:55 + --> $DIR/use_self.rs:98:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:112:13 + --> $DIR/use_self.rs:113:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:147:29 + --> $DIR/use_self.rs:148:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:148:21 + --> $DIR/use_self.rs:149:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:159:21 + --> $DIR/use_self.rs:160:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:160:13 + --> $DIR/use_self.rs:161:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:177:21 + --> $DIR/use_self.rs:178:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:178:21 + --> $DIR/use_self.rs:179:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:179:21 + --> $DIR/use_self.rs:180:21 | LL | let _ = Enum::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 | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:222:13 + --> $DIR/use_self.rs:223:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:224:13 + --> $DIR/use_self.rs:225:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:243:13 + --> $DIR/use_self.rs:244:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:257:25 + --> $DIR/use_self.rs:258:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:258:13 + --> $DIR/use_self.rs:259:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:262:16 + --> $DIR/use_self.rs:263:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:262:22 + --> $DIR/use_self.rs:263:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:285:29 + --> $DIR/use_self.rs:286:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:286:13 + --> $DIR/use_self.rs:287:13 | LL | Foo:: { value } | ^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:458:13 + --> $DIR/use_self.rs:459:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:495:13 + --> $DIR/use_self.rs:496:13 | LL | S2::new() | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:532:17 + --> $DIR/use_self.rs:533:17 | LL | Foo::Bar => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:533:17 + --> $DIR/use_self.rs:534:17 | LL | Foo::Baz => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:539:20 + --> $DIR/use_self.rs:540:20 | LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:563:17 + --> $DIR/use_self.rs:564:17 | LL | Something::Num(n) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:564:17 + --> $DIR/use_self.rs:565:17 | LL | Something::TupleNums(n, _m) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:565:17 + --> $DIR/use_self.rs:566:17 | LL | Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:571:17 + --> $DIR/use_self.rs:572:17 | LL | crate::issue8845::Something::Num(n) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:572:17 + --> $DIR/use_self.rs:573:17 | LL | crate::issue8845::Something::TupleNums(n, _m) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:573:17 + --> $DIR/use_self.rs:574:17 | LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:589:17 + --> $DIR/use_self.rs:590:17 | LL | let Foo(x) = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:594:17 + --> $DIR/use_self.rs:595:17 | LL | let crate::issue8845::Foo(x) = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:601:17 + --> $DIR/use_self.rs:602:17 | LL | let Bar { x, .. } = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:606:17 + --> $DIR/use_self.rs:607:17 | LL | let crate::issue8845::Bar { x, .. } = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` -error: aborting due to 41 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:648:17 + | +LL | E::A => {}, + | ^ help: use the applicable keyword: `Self` + +error: aborting due to 42 previous errors diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 9e07769a8e4fd..a6d8d0307ce53 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -48,7 +48,7 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`. let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string()); let rustc_version = String::from_utf8( - std::process::Command::new(&rustc) + std::process::Command::new(rustc) .arg("--version") .output() .expect("failed to run `rustc --version`") diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index c5d602ea3035f..e46ad2c6e0eec 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -442,6 +442,12 @@

Clippy Lints

All +
  • + +
  • Citizen - Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their - definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. -* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or - made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation - team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a - safe place for you and we've got your back. -* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. - -## Moderation - - -These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, -please contact the [Rust moderation team][mod_team]. - -1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, - are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) -2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. -3. Moderators will first respond to such remarks with a warning. -4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. -5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. -6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended - party a genuine apology. -7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a - different moderator, **in private**. Complaints about bans in-channel are not allowed. -8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate - situation, they should expect less leeway than others. - -In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically -unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly -if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can -drive people away from the community entirely. - -And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was -they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good -there was something you could've communicated better — remember that it's your responsibility to make your fellow -Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about -cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their -trust. - -The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust, -#rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo); -GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org -(users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the -maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider -explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. - -*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the -[Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* - -[mod_team]: https://www.rust-lang.org/team.html#Moderation-team +The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html). From e4540ad65fa14ceebd5145ab771fe4918d170bf1 Mon Sep 17 00:00:00 2001 From: koka Date: Mon, 24 Oct 2022 23:49:59 +0900 Subject: [PATCH 096/169] feat: implement manual_is_ascii_check lint modify fix: allow unused in test code fix: types in doc comment Update clippy_lints/src/manual_is_ascii_check.rs Co-authored-by: Fridtjof Stoldt Update clippy_lints/src/manual_is_ascii_check.rs Co-authored-by: Fridtjof Stoldt Update clippy_lints/src/manual_is_ascii_check.rs Co-authored-by: Fridtjof Stoldt fix ui test result fix: unnecessary format! chore: apply feedbacks * check msrvs also for const fn * check applicability manually * modify documents --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/manual_is_ascii_check.rs | 157 ++++++++++++++++++++++ clippy_utils/src/msrvs.rs | 2 +- tests/ui/manual_is_ascii_check.fixed | 45 +++++++ tests/ui/manual_is_ascii_check.rs | 45 +++++++ tests/ui/manual_is_ascii_check.stderr | 70 ++++++++++ 8 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/manual_is_ascii_check.rs create mode 100644 tests/ui/manual_is_ascii_check.fixed create mode 100644 tests/ui/manual_is_ascii_check.rs create mode 100644 tests/ui/manual_is_ascii_check.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 35864e85673a0..d75ef52e0ee4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3997,6 +3997,7 @@ Released 2018-09-13 [`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_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed +[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else [`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 diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index f4fc2bd333099..448b12f1b0a03 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -251,6 +251,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::manual_bits::MANUAL_BITS_INFO, crate::manual_clamp::MANUAL_CLAMP_INFO, crate::manual_instant_elapsed::MANUAL_INSTANT_ELAPSED_INFO, + crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 197d8f2dc0810..fe05f2a13f9a5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -173,6 +173,7 @@ mod manual_async_fn; mod manual_bits; mod manual_clamp; mod manual_instant_elapsed; +mod manual_is_ascii_check; mod manual_let_else; mod manual_non_exhaustive; mod manual_rem_euclid; @@ -918,6 +919,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); + store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs new file mode 100644 index 0000000000000..3a6b693f7662c --- /dev/null +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -0,0 +1,157 @@ +use rustc_ast::LitKind::{Byte, Char}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{def_id::DefId, sym}; + +use clippy_utils::{ + diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet, +}; + +declare_clippy_lint! { + /// ### What it does + /// Suggests to use dedicated built-in methods, + /// `is_ascii_(lowercase|uppercase|digit)` for checking on corresponding ascii range + /// + /// ### Why is this bad? + /// Using the built-in functions is more readable and makes it + /// clear that it's not a specific subset of characters, but all + /// ASCII (lowercase|uppercase|digit) characters. + /// ### Example + /// ```rust + /// fn main() { + /// assert!(matches!('x', 'a'..='z')); + /// assert!(matches!(b'X', b'A'..=b'Z')); + /// assert!(matches!('2', '0'..='9')); + /// assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// assert!('x'.is_ascii_lowercase()); + /// assert!(b'X'.is_ascii_uppercase()); + /// assert!('2'.is_ascii_digit()); + /// assert!('x'.is_ascii_alphabetic()); + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub MANUAL_IS_ASCII_CHECK, + style, + "use dedicated method to check ascii range" +} +impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]); + +pub struct ManualIsAsciiCheck { + msrv: Option, +} + +impl ManualIsAsciiCheck { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +#[derive(Debug, PartialEq)] +enum CharRange { + /// 'a'..='z' | b'a'..=b'z' + LowerChar, + /// 'A'..='Z' | b'A'..=b'Z' + UpperChar, + /// AsciiLower | AsciiUpper + FullChar, + /// '0..=9' + Digit, + Otherwise, +} + +impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) { + return; + } + + if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) { + return; + } + + let Some(macro_call) = root_macro_call(expr.span) else { return }; + + if is_matches_macro(cx, macro_call.def_id) { + if let ExprKind::Match(recv, [arm, ..], _) = expr.kind { + let range = check_pat(&arm.pat.kind); + + if let Some(sugg) = match range { + CharRange::UpperChar => Some("is_ascii_uppercase"), + CharRange::LowerChar => Some("is_ascii_lowercase"), + CharRange::FullChar => Some("is_ascii_alphabetic"), + CharRange::Digit => Some("is_ascii_digit"), + CharRange::Otherwise => None, + } { + let mut applicability = Applicability::MaybeIncorrect; + let default_snip = ".."; + // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for + // macro span, so we check applicability manually by comaring `recv` is not default. + let recv = snippet(cx, recv.span, default_snip); + + if recv != default_snip { + applicability = Applicability::MachineApplicable; + } + + span_lint_and_sugg( + cx, + MANUAL_IS_ASCII_CHECK, + macro_call.span, + "manual check for common ascii range", + "try", + format!("{recv}.{sugg}()"), + applicability, + ); + } + } + } + } + + extract_msrv_attr!(LateContext); +} + +fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { + match pat_kind { + PatKind::Or(pats) => { + let ranges = pats.iter().map(|p| check_pat(&p.kind)).collect::>(); + + if ranges.len() == 2 && ranges.contains(&CharRange::UpperChar) && ranges.contains(&CharRange::LowerChar) { + CharRange::FullChar + } else { + CharRange::Otherwise + } + }, + PatKind::Range(Some(start), Some(end), kind) if *kind == RangeEnd::Included => check_range(start, end), + _ => CharRange::Otherwise, + } +} + +fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { + if let ExprKind::Lit(start_lit) = &start.kind + && let ExprKind::Lit(end_lit) = &end.kind { + match (&start_lit.node, &end_lit.node) { + (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, + (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, + (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, + _ => CharRange::Otherwise, + } + } else { + CharRange::Otherwise + } +} + +fn is_matches_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool { + if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) { + return sym::matches_macro == name; + } + + false +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 9a672e2ddfd7d..79b19e6fb3eb0 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -19,7 +19,7 @@ msrv_aliases! { 1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST } 1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS } 1,50,0 { BOOL_THEN, CLAMP } - 1,47,0 { TAU } + 1,47,0 { TAU, IS_ASCII_DIGIT_CONST } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } 1,43,0 { LOG2_10, LOG10_2 } diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed new file mode 100644 index 0000000000000..765bb785994e2 --- /dev/null +++ b/tests/ui/manual_is_ascii_check.fixed @@ -0,0 +1,45 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![allow(unused, dead_code)] +#![warn(clippy::manual_is_ascii_check)] + +fn main() { + assert!('x'.is_ascii_lowercase()); + assert!('X'.is_ascii_uppercase()); + assert!(b'x'.is_ascii_lowercase()); + assert!(b'X'.is_ascii_uppercase()); + + let num = '2'; + assert!(num.is_ascii_digit()); + assert!(b'1'.is_ascii_digit()); + assert!('x'.is_ascii_alphabetic()); + + assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); +} + +fn msrv_1_23() { + #![clippy::msrv = "1.23"] + + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); +} + +fn msrv_1_24() { + #![clippy::msrv = "1.24"] + + assert!(b'1'.is_ascii_digit()); + assert!('X'.is_ascii_uppercase()); + assert!('x'.is_ascii_alphabetic()); +} + +fn msrv_1_46() { + #![clippy::msrv = "1.46"] + const FOO: bool = matches!('x', '0'..='9'); +} + +fn msrv_1_47() { + #![clippy::msrv = "1.47"] + const FOO: bool = 'x'.is_ascii_digit(); +} diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs new file mode 100644 index 0000000000000..be13316104123 --- /dev/null +++ b/tests/ui/manual_is_ascii_check.rs @@ -0,0 +1,45 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![allow(unused, dead_code)] +#![warn(clippy::manual_is_ascii_check)] + +fn main() { + assert!(matches!('x', 'a'..='z')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!(b'x', b'a'..=b'z')); + assert!(matches!(b'X', b'A'..=b'Z')); + + let num = '2'; + assert!(matches!(num, '0'..='9')); + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + + assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); +} + +fn msrv_1_23() { + #![clippy::msrv = "1.23"] + + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); +} + +fn msrv_1_24() { + #![clippy::msrv = "1.24"] + + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); +} + +fn msrv_1_46() { + #![clippy::msrv = "1.46"] + const FOO: bool = matches!('x', '0'..='9'); +} + +fn msrv_1_47() { + #![clippy::msrv = "1.47"] + const FOO: bool = matches!('x', '0'..='9'); +} diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr new file mode 100644 index 0000000000000..c0a9d4db1a159 --- /dev/null +++ b/tests/ui/manual_is_ascii_check.stderr @@ -0,0 +1,70 @@ +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:8:13 + | +LL | assert!(matches!('x', 'a'..='z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_lowercase()` + | + = note: `-D clippy::manual-is-ascii-check` implied by `-D warnings` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:9:13 + | +LL | assert!(matches!('X', 'A'..='Z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:10:13 + | +LL | assert!(matches!(b'x', b'a'..=b'z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'x'.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:11:13 + | +LL | assert!(matches!(b'X', b'A'..=b'Z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'X'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:14:13 + | +LL | assert!(matches!(num, '0'..='9')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:15:13 + | +LL | assert!(matches!(b'1', b'0'..=b'9')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:16:13 + | +LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:32:13 + | +LL | assert!(matches!(b'1', b'0'..=b'9')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:33:13 + | +LL | assert!(matches!('X', 'A'..='Z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:34:13 + | +LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:44:23 + | +LL | const FOO: bool = matches!('x', '0'..='9'); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()` + +error: aborting due to 11 previous errors + From b0dcb862c60c87c55f4e34c2ecce8914ffe3c4c7 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 22 Apr 2022 18:49:15 -0400 Subject: [PATCH 097/169] Move `needless_collect` into the `methods` lint pass --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/loops/mod.rs | 26 ------------------- clippy_lints/src/methods/mod.rs | 26 +++++++++++++++++++ .../{loops => methods}/needless_collect.rs | 0 4 files changed, 27 insertions(+), 27 deletions(-) rename clippy_lints/src/{loops => methods}/needless_collect.rs (100%) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 747636b7ec390..e9094ca3a093e 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -237,7 +237,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::loops::MANUAL_MEMCPY_INFO, crate::loops::MISSING_SPIN_LOOP_INFO, crate::loops::MUT_RANGE_BOUND_INFO, - crate::loops::NEEDLESS_COLLECT_INFO, crate::loops::NEEDLESS_RANGE_LOOP_INFO, crate::loops::NEVER_LOOP_INFO, crate::loops::SAME_ITEM_PUSH_INFO, @@ -346,6 +345,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::MAP_UNWRAP_OR_INFO, crate::methods::MUT_MUTEX_LOCK_INFO, crate::methods::NAIVE_BYTECOUNT_INFO, + crate::methods::NEEDLESS_COLLECT_INFO, crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO, crate::methods::NEEDLESS_OPTION_TAKE_INFO, crate::methods::NEEDLESS_SPLITN_INFO, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 821fe17302391..8e52cac4323c3 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -9,7 +9,6 @@ mod manual_flatten; mod manual_memcpy; mod missing_spin_loop; mod mut_range_bound; -mod needless_collect; mod needless_range_loop; mod never_loop; mod same_item_push; @@ -205,28 +204,6 @@ declare_clippy_lint! { "`loop { if let { ... } else break }`, which can be written as a `while let` loop" } -declare_clippy_lint! { - /// ### What it does - /// Checks for functions collecting an iterator when collect - /// is not needed. - /// - /// ### Why is this bad? - /// `collect` causes the allocation of a new data structure, - /// when this allocation may not be needed. - /// - /// ### Example - /// ```rust - /// # let iterator = vec![1].into_iter(); - /// let len = iterator.clone().collect::>().len(); - /// // should be - /// let len = iterator.count(); - /// ``` - #[clippy::version = "1.30.0"] - pub NEEDLESS_COLLECT, - nursery, - "collecting an iterator when collect is not needed" -} - declare_clippy_lint! { /// ### What it does /// Checks `for` loops over slices with an explicit counter @@ -605,7 +582,6 @@ declare_lint_pass!(Loops => [ EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, WHILE_LET_LOOP, - NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, EMPTY_LOOP, WHILE_LET_ON_ITERATOR, @@ -667,8 +643,6 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_immutable_condition::check(cx, condition, body); missing_spin_loop::check(cx, condition, body); } - - needless_collect::check(expr, cx); } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 26834dc4fcc61..c413d9baf1722 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -54,6 +54,7 @@ mod map_flatten; mod map_identity; mod map_unwrap_or; mod mut_mutex_lock; +mod needless_collect; mod needless_option_as_deref; mod needless_option_take; mod no_effect_replace; @@ -3141,6 +3142,28 @@ declare_clippy_lint! { "jumping to the start of stream using `seek` method" } +declare_clippy_lint! { + /// ### What it does + /// Checks for functions collecting an iterator when collect + /// is not needed. + /// + /// ### Why is this bad? + /// `collect` causes the allocation of a new data structure, + /// when this allocation may not be needed. + /// + /// ### Example + /// ```rust + /// # let iterator = vec![1].into_iter(); + /// let len = iterator.clone().collect::>().len(); + /// // should be + /// let len = iterator.count(); + /// ``` + #[clippy::version = "1.30.0"] + pub NEEDLESS_COLLECT, + nursery, + "collecting an iterator when collect is not needed" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -3267,6 +3290,7 @@ impl_lint_pass!(Methods => [ ITER_KV_MAP, SEEK_FROM_CURRENT, SEEK_TO_START_INSTEAD_OF_REWIND, + NEEDLESS_COLLECT, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3317,6 +3341,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, _ => (), } + + needless_collect::check(expr, cx); } #[allow(clippy::too_many_lines)] diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs similarity index 100% rename from clippy_lints/src/loops/needless_collect.rs rename to clippy_lints/src/methods/needless_collect.rs From 586bd3f735a7ec8c0ee03ab3515dfd8a89a31ff4 Mon Sep 17 00:00:00 2001 From: kraktus Date: Sun, 23 Oct 2022 14:00:44 +0200 Subject: [PATCH 098/169] refactor to avoid potential ICE --- clippy_lints/src/excessive_bools.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 453471c8cdda6..e1911449242df 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -141,14 +141,13 @@ impl EarlyLintPass for ExcessiveBools { return; } - let struct_bools = variant_data + if let Ok(struct_bools) = variant_data .fields() .iter() .filter(|field| is_bool_ty(&field.ty)) .count() - .try_into() - .unwrap(); - if self.max_struct_bools < struct_bools { + .try_into() && self.max_struct_bools < struct_bools + { span_lint_and_help( cx, STRUCT_EXCESSIVE_BOOLS, @@ -156,8 +155,8 @@ impl EarlyLintPass for ExcessiveBools { &format!("more than {} bools in a struct", self.max_struct_bools), None, "consider using a state machine or refactoring bools into two-variant enums", - ); - } + ) + } }, ItemKind::Impl(box Impl { of_trait: None, items, .. From 9c69e93595e581a92c8385b633e687393cb07471 Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 28 Oct 2022 14:54:59 +0200 Subject: [PATCH 099/169] Rewrite `ExcessiveBools` to be a `LateLintPass` lint changelog: [`fn_params_excessive_bools`] Make it possible to allow the lint at the method level --- clippy_lints/src/excessive_bools.rs | 126 +++++++++++----------- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/methods/mod.rs | 13 +-- clippy_utils/src/hir_utils.rs | 10 +- clippy_utils/src/lib.rs | 2 +- tests/ui/fn_params_excessive_bools.rs | 1 + tests/ui/fn_params_excessive_bools.stderr | 20 ++-- 7 files changed, 90 insertions(+), 84 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index e1911449242df..08164955c6a34 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,8 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use clippy_utils::{get_parent_node, is_bool}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, Node, Ty}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; +use rustc_target::spec::abi::Abi; declare_clippy_lint! { /// ### What it does @@ -83,6 +86,12 @@ pub struct ExcessiveBools { max_fn_params_bools: u64, } +#[derive(Eq, PartialEq, Debug)] +enum Kind { + Struct, + Fn, +} + impl ExcessiveBools { #[must_use] pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self { @@ -92,21 +101,20 @@ impl ExcessiveBools { } } - fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) { - match fn_sig.header.ext { - Extern::Implicit(_) | Extern::Explicit(_, _) => return, - Extern::None => (), + fn too_many_bools<'tcx>(&self, tys: impl Iterator>, kind: Kind) -> bool { + if let Ok(bools) = tys.filter(|ty| is_bool(ty)).count().try_into() { + (if Kind::Fn == kind { + self.max_fn_params_bools + } else { + self.max_struct_bools + }) < bools + } else { + false } + } - let fn_sig_bools = fn_sig - .decl - .inputs - .iter() - .filter(|param| is_bool_ty(¶m.ty)) - .count() - .try_into() - .unwrap(); - if self.max_fn_params_bools < fn_sig_bools { + fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) { + if self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) { span_lint_and_help( cx, FN_PARAMS_EXCESSIVE_BOOLS, @@ -121,55 +129,53 @@ impl ExcessiveBools { impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); -fn is_bool_ty(ty: &Ty) -> bool { - if let TyKind::Path(None, path) = &ty.kind { - if let [name] = path.segments.as_slice() { - return name.ident.name == sym::bool; - } - } - false -} - -impl EarlyLintPass for ExcessiveBools { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { +impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if item.span.from_expansion() { return; } - match &item.kind { - ItemKind::Struct(variant_data, _) => { - if item.attrs.iter().any(|attr| attr.has_name(sym::repr)) { - return; - } + if let ItemKind::Struct(variant_data, _) = &item.kind { + if cx + .tcx + .hir() + .attrs(item.hir_id()) + .iter() + .any(|attr| attr.has_name(sym::repr)) + { + return; + } + + if self.too_many_bools(variant_data.fields().iter().map(|field| field.ty), Kind::Struct) { + span_lint_and_help( + cx, + STRUCT_EXCESSIVE_BOOLS, + item.span, + &format!("more than {} bools in a struct", self.max_struct_bools), + None, + "consider using a state machine or refactoring bools into two-variant enums", + ) + } + } + } - if let Ok(struct_bools) = variant_data - .fields() - .iter() - .filter(|field| is_bool_ty(&field.ty)) - .count() - .try_into() && self.max_struct_bools < struct_bools - { - span_lint_and_help( - cx, - STRUCT_EXCESSIVE_BOOLS, - item.span, - &format!("more than {} bools in a struct", self.max_struct_bools), - None, - "consider using a state machine or refactoring bools into two-variant enums", - ) - } - }, - ItemKind::Impl(box Impl { - of_trait: None, items, .. - }) - | ItemKind::Trait(box Trait { items, .. }) => { - for item in items { - if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { - self.check_fn_sig(cx, sig, item.span); - } - } - }, - ItemKind::Fn(box Fn { sig, .. }) => self.check_fn_sig(cx, sig, item.span), - _ => (), + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'tcx>, + _: &'tcx Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if let Some(fn_header) = fn_kind.header() + && fn_header.abi == Abi::Rust + && if let Some(Node::Item(item)) = get_parent_node(cx.tcx, hir_id) { + !matches!(item.kind, ItemKind::ExternCrate(..)) + } else { + true + } + && !span.from_expansion() { + self.check_fn_sig(cx, fn_decl, span) } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 01da11958e2a6..e96075a2673f1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -793,7 +793,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(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 || { + store.register_late_pass(move |_| { Box::new(excessive_bools::ExcessiveBools::new( max_struct_bools, max_fn_params_bools, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6665329d1148f..996d4cb412344 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -104,11 +104,10 @@ use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; +use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind}; +use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -3966,14 +3965,6 @@ impl OutType { } } -fn is_bool(ty: &hir::Ty<'_>) -> bool { - if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind { - matches!(path.res, Res::PrimTy(PrimTy::Bool)) - } else { - false - } -} - fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { expected.constness == actual.constness && expected.unsafety == actual.unsafety diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 02b973e5b2785..da8c976c952c9 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -8,7 +8,7 @@ use rustc_hir::HirIdMap; use rustc_hir::{ ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, - PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, + PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; @@ -1030,6 +1030,14 @@ pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 { h.finish() } +pub fn is_bool(ty: &Ty<'_>) -> bool { + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + matches!(path.res, Res::PrimTy(PrimTy::Bool)) + } else { + false + } +} + pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 { let mut h = SpanlessHash::new(cx); h.hash_expr(e); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index dda4660fb7a3f..0000896fdb652 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -66,7 +66,7 @@ pub mod visitors; pub use self::attrs::*; pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match}; pub use self::hir_utils::{ - both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash, + both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash, }; use core::ops::ControlFlow; diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs index f805bcc9ba8af..4fcd4d54ce628 100644 --- a/tests/ui/fn_params_excessive_bools.rs +++ b/tests/ui/fn_params_excessive_bools.rs @@ -2,6 +2,7 @@ #![allow(clippy::too_many_arguments)] extern "C" { + // Should not lint, most of the time users have no control over extern function signatures fn f(_: bool, _: bool, _: bool, _: bool); } diff --git a/tests/ui/fn_params_excessive_bools.stderr b/tests/ui/fn_params_excessive_bools.stderr index 11627105691b7..9d22d3851d2fd 100644 --- a/tests/ui/fn_params_excessive_bools.stderr +++ b/tests/ui/fn_params_excessive_bools.stderr @@ -1,5 +1,5 @@ error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:18:1 + --> $DIR/fn_params_excessive_bools.rs:19:1 | LL | fn g(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | fn g(_: bool, _: bool, _: bool, _: bool) {} = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings` error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:21:1 + --> $DIR/fn_params_excessive_bools.rs:22:1 | LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,23 +16,23 @@ LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:25:5 + --> $DIR/fn_params_excessive_bools.rs:31:5 | -LL | fn f(_: bool, _: bool, _: bool, _: bool); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:30:5 + --> $DIR/fn_params_excessive_bools.rs:38:5 | -LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn f(_: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:42:5 + --> $DIR/fn_params_excessive_bools.rs:43:5 | LL | / fn n(_: bool, _: u32, _: bool, _: Box, _: bool, _: bool) { LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {} @@ -42,7 +42,7 @@ LL | | } = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:43:9 + --> $DIR/fn_params_excessive_bools.rs:44:9 | LL | fn nn(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From e8c2c3d1c6ac27d0f1fa9125b79f1f927a59cfcf Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 28 Oct 2022 14:57:51 +0200 Subject: [PATCH 100/169] refactor `has_repr_attr` --- clippy_lints/src/excessive_bools.rs | 21 +++++---------------- clippy_lints/src/trailing_empty_array.rs | 8 ++------ clippy_utils/src/lib.rs | 4 ++++ 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 08164955c6a34..08fcf304958f1 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{get_parent_node, is_bool}; +use clippy_utils::{has_repr_attr, is_bool}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, Node, Ty}; +use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, Ty}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, Span}; +use rustc_span::Span; use rustc_target::spec::abi::Abi; declare_clippy_lint! { @@ -135,13 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { return; } if let ItemKind::Struct(variant_data, _) = &item.kind { - if cx - .tcx - .hir() - .attrs(item.hir_id()) - .iter() - .any(|attr| attr.has_name(sym::repr)) - { + if has_repr_attr(cx, item.hir_id()) { return; } @@ -165,15 +159,10 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { fn_decl: &'tcx FnDecl<'tcx>, _: &'tcx Body<'tcx>, span: Span, - hir_id: HirId, + _: HirId, ) { if let Some(fn_header) = fn_kind.header() && fn_header.abi == Abi::Rust - && if let Some(Node::Item(item)) = get_parent_node(cx.tcx, hir_id) { - !matches!(item.kind, ItemKind::ExternCrate(..)) - } else { - true - } && !span.from_expansion() { self.check_fn_sig(cx, fn_decl, span) } diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs index 58cc057a39ed9..712a7a6601ac4 100644 --- a/clippy_lints/src/trailing_empty_array.rs +++ b/clippy_lints/src/trailing_empty_array.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::{HirId, Item, ItemKind}; +use clippy_utils::has_repr_attr; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Const; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -72,7 +72,3 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_ } } } - -fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { - cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::repr)) -} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0000896fdb652..7ce8bbc1b7b33 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1780,6 +1780,10 @@ pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool { attrs.iter().any(|attr| attr.has_name(symbol)) } +pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { + has_attr(cx.tcx.hir().attrs(hir_id), sym::repr) +} + pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool { let map = &tcx.hir(); let mut prev_enclosing_node = None; From d9b940e2c36eee3172c0f41f570de9e2d8ed95fd Mon Sep 17 00:00:00 2001 From: kraktus Date: Sun, 23 Oct 2022 16:03:05 +0200 Subject: [PATCH 101/169] dogfood --- clippy_lints/src/excessive_bools.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 08fcf304958f1..281d0631d06a0 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -86,7 +86,7 @@ pub struct ExcessiveBools { max_fn_params_bools: u64, } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, Copy, Clone)] enum Kind { Struct, Fn, @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { &format!("more than {} bools in a struct", self.max_struct_bools), None, "consider using a state machine or refactoring bools into two-variant enums", - ) + ); } } } @@ -164,7 +164,7 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { if let Some(fn_header) = fn_kind.header() && fn_header.abi == Abi::Rust && !span.from_expansion() { - self.check_fn_sig(cx, fn_decl, span) + self.check_fn_sig(cx, fn_decl, span); } } } From 9e1c7febe261b834d2c787aca2fcd9de577d76d8 Mon Sep 17 00:00:00 2001 From: kraktus Date: Sat, 29 Oct 2022 16:12:41 +0200 Subject: [PATCH 102/169] `excessive_bools`, do not lint in trait impls Should not lint because the trait might not be changeable by the user We only lint in the trait definition --- clippy_lints/src/excessive_bools.rs | 11 ++++++++--- tests/ui/fn_params_excessive_bools.rs | 2 ++ tests/ui/fn_params_excessive_bools.stderr | 14 +++----------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 281d0631d06a0..68c5e5ab1f1eb 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::{has_repr_attr, is_bool}; +use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool}; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, Ty}; use rustc_lint::{LateContext, LateLintPass}; @@ -159,11 +159,16 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { fn_decl: &'tcx FnDecl<'tcx>, _: &'tcx Body<'tcx>, span: Span, - _: HirId, + hir_id: HirId, ) { if let Some(fn_header) = fn_kind.header() && fn_header.abi == Abi::Rust - && !span.from_expansion() { + && !span.from_expansion() + && get_parent_as_impl(cx.tcx, hir_id) + .map_or(true, + |impl_item| impl_item.of_trait.is_none() + ) + { self.check_fn_sig(cx, fn_decl, span); } } diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs index 4fcd4d54ce628..2635e433a319e 100644 --- a/tests/ui/fn_params_excessive_bools.rs +++ b/tests/ui/fn_params_excessive_bools.rs @@ -35,6 +35,8 @@ impl S { } impl Trait for S { + // Should not lint because the trait might not be changeable by the user + // We only lint in the trait definition fn f(_: bool, _: bool, _: bool, _: bool) {} fn g(_: bool, _: bool, _: bool, _: Vec) {} } diff --git a/tests/ui/fn_params_excessive_bools.stderr b/tests/ui/fn_params_excessive_bools.stderr index 9d22d3851d2fd..7edfd07b9db3f 100644 --- a/tests/ui/fn_params_excessive_bools.stderr +++ b/tests/ui/fn_params_excessive_bools.stderr @@ -24,15 +24,7 @@ LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:38:5 - | -LL | fn f(_: bool, _: bool, _: bool, _: bool) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider refactoring bools into two-variant enums - -error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:43:5 + --> $DIR/fn_params_excessive_bools.rs:45:5 | LL | / fn n(_: bool, _: u32, _: bool, _: Box, _: bool, _: bool) { LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {} @@ -42,12 +34,12 @@ LL | | } = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:44:9 + --> $DIR/fn_params_excessive_bools.rs:46:9 | LL | fn nn(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider refactoring bools into two-variant enums -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors From 13b737d5b89c374d7d0c9b2f3968654b9c5ba370 Mon Sep 17 00:00:00 2001 From: kraktus Date: Sat, 29 Oct 2022 17:43:43 +0200 Subject: [PATCH 103/169] [`fn_params_excessive_bools`] whitelist in signle function in test --- tests/ui/fn_params_excessive_bools.rs | 3 +++ tests/ui/fn_params_excessive_bools.stderr | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs index 2635e433a319e..7995912110d6e 100644 --- a/tests/ui/fn_params_excessive_bools.rs +++ b/tests/ui/fn_params_excessive_bools.rs @@ -25,6 +25,8 @@ struct S; trait Trait { fn f(_: bool, _: bool, _: bool, _: bool); fn g(_: bool, _: bool, _: bool, _: Vec); + #[allow(clippy::fn_params_excessive_bools)] + fn h(_: bool, _: bool, _: bool, _: bool, _: bool, _: bool); } impl S { @@ -39,6 +41,7 @@ impl Trait for S { // We only lint in the trait definition fn f(_: bool, _: bool, _: bool, _: bool) {} fn g(_: bool, _: bool, _: bool, _: Vec) {} + fn h(_: bool, _: bool, _: bool, _: bool, _: bool, _: bool) {} } fn main() { diff --git a/tests/ui/fn_params_excessive_bools.stderr b/tests/ui/fn_params_excessive_bools.stderr index 7edfd07b9db3f..8c2383873fe9d 100644 --- a/tests/ui/fn_params_excessive_bools.stderr +++ b/tests/ui/fn_params_excessive_bools.stderr @@ -16,7 +16,7 @@ LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:31:5 + --> $DIR/fn_params_excessive_bools.rs:33:5 | LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:45:5 + --> $DIR/fn_params_excessive_bools.rs:48:5 | LL | / fn n(_: bool, _: u32, _: bool, _: Box, _: bool, _: bool) { LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {} @@ -34,7 +34,7 @@ LL | | } = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:46:9 + --> $DIR/fn_params_excessive_bools.rs:49:9 | LL | fn nn(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 8bfc8bc5e0210dabd2976edad5bf285a96dcb360 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 20 Apr 2022 16:10:18 -0400 Subject: [PATCH 104/169] Lint `needless_collect` on non-std collection types --- .../src/methods/collapsible_str_replace.rs | 6 +- clippy_lints/src/methods/manual_str_repeat.rs | 2 - .../src/methods/map_collect_result_unit.rs | 15 +- clippy_lints/src/methods/mod.rs | 95 ++++---- clippy_lints/src/methods/needless_collect.rs | 223 +++++++++++------- clippy_utils/src/ty.rs | 134 ++++++++++- tests/ui/needless_collect.fixed | 29 +++ tests/ui/needless_collect.rs | 29 +++ tests/ui/needless_collect.stderr | 26 +- 9 files changed, 408 insertions(+), 151 deletions(-) diff --git a/clippy_lints/src/methods/collapsible_str_replace.rs b/clippy_lints/src/methods/collapsible_str_replace.rs index 501646863fe15..ac61b4377885b 100644 --- a/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/clippy_lints/src/methods/collapsible_str_replace.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( // If the parent node's `to` argument is the same as the `to` argument // of the last replace call in the current chain, don't lint as it was already linted if let Some(parent) = get_parent_expr(cx, expr) - && let Some(("replace", _, [current_from, current_to], _)) = method_call(parent) + && let Some(("replace", _, [current_from, current_to], _, _)) = method_call(parent) && eq_expr_value(cx, to, current_to) && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind() { @@ -48,7 +48,7 @@ fn collect_replace_calls<'tcx>( let mut from_args = VecDeque::new(); let _: Option<()> = for_each_expr(expr, |e| { - if let Some(("replace", _, [from, to], _)) = method_call(e) { + if let Some(("replace", _, [from, to], _, _)) = method_call(e) { if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { methods.push_front(e); from_args.push_front(from); @@ -78,7 +78,7 @@ fn check_consecutive_replace_calls<'tcx>( .collect(); let app = Applicability::MachineApplicable; let earliest_replace_call = replace_methods.methods.front().unwrap(); - if let Some((_, _, [..], span_lo)) = method_call(earliest_replace_call) { + if let Some((_, _, [..], span_lo, _)) = method_call(earliest_replace_call) { span_lint_and_sugg( cx, COLLAPSIBLE_STR_REPLACE, diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 8b6b8f1bf16cb..8b798fdb12fc9 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -59,10 +59,8 @@ pub(super) fn check( if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String); - 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(); diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index d420f144eea1f..a0300d278709d 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; @@ -11,18 +10,10 @@ use rustc_span::symbol::sym; use super::MAP_COLLECT_RESULT_UNIT; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - iter: &hir::Expr<'_>, - map_fn: &hir::Expr<'_>, - collect_recv: &hir::Expr<'_>, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) { + // return of collect `Result<(),_>` + let collect_ret_ty = cx.typeck_results().expr_ty(expr); if_chain! { - // called on Iterator - if is_trait_method(cx, collect_recv, sym::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); if let ty::Adt(_, substs) = collect_ret_ty.kind(); if let Some(result_t) = substs.types().next(); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c413d9baf1722..1c0bbe086f7c8 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3296,11 +3296,11 @@ impl_lint_pass!(Methods => [ /// Extracts a method call name, args, and `Span` of the method name. fn method_call<'tcx>( recv: &'tcx hir::Expr<'tcx>, -) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span)> { - if let ExprKind::MethodCall(path, receiver, args, _) = recv.kind { +) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span, Span)> { + if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind { if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() { let name = path.ident.name.as_str(); - return Some((name, receiver, args, path.ident.span)); + return Some((name, receiver, args, path.ident.span, call_span)); } } None @@ -3341,8 +3341,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, _ => (), } - - needless_collect::check(expr, cx); } #[allow(clippy::too_many_lines)] @@ -3488,7 +3486,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { impl Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some((name, recv, args, span)) = method_call(expr) { + if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); @@ -3507,28 +3505,31 @@ impl Methods { ("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, self.msrv), - ("collect", []) => match method_call(recv) { - Some((name @ ("cloned" | "copied"), recv2, [], _)) => { - iter_cloned_collect::check(cx, name, expr, recv2); - }, - 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(self.msrv, msrvs::STR_REPEAT) { - manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); - } - }, - _ => {}, + ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { + needless_collect::check(cx, span, expr, recv, call_span); + match method_call(recv) { + Some((name @ ("cloned" | "copied"), recv2, [], _, _)) => { + iter_cloned_collect::check(cx, name, expr, recv2); + }, + Some(("map", m_recv, [m_arg], _, _)) => { + map_collect_result_unit::check(cx, expr, m_recv, m_arg); + }, + Some(("take", take_self_arg, [take_arg], _, _)) => { + if meets_msrv(self.msrv, msrvs::STR_REPEAT) { + manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); + } + }, + _ => {}, + } }, ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), - Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _)) => { + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), + Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => { iter_count::check(cx, expr, recv2, name2); }, - Some(("map", _, [arg], _)) => suspicious_map::check(cx, expr, recv, arg), - Some(("filter", recv2, [arg], _)) => bytecount::check(cx, expr, recv2, arg), - Some(("bytes", recv2, [], _)) => bytes_count_to_len::check(cx, expr, recv, recv2), + Some(("map", _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), + Some(("filter", recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), + Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), _ => {}, }, ("drain", [arg]) => { @@ -3540,8 +3541,8 @@ impl Methods { } }, ("expect", [_]) => match method_call(recv) { - Some(("ok", recv, [], _)) => ok_expect::check(cx, expr, recv), - Some(("err", recv, [], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), + Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv), + Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests), }, ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests), @@ -3561,13 +3562,13 @@ impl Methods { flat_map_option::check(cx, expr, arg, span); }, ("flatten", []) => match method_call(recv) { - Some(("map", recv, [map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true), + Some(("map", recv, [map_arg], map_span, _)) => map_flatten::check(cx, expr, recv, map_arg, map_span), + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true), _ => {}, }, ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), ("for_each", [_]) => { - if let Some(("inspect", _, [_], span2)) = method_call(recv) { + if let Some(("inspect", _, [_], span2, _)) = method_call(recv) { inspect_for_each::check(cx, expr, span2); } }, @@ -3587,12 +3588,12 @@ impl Methods { iter_on_single_or_empty_collections::check(cx, expr, name, recv); }, ("join", [join_arg]) => { - if let Some(("collect", _, _, span)) = method_call(recv) { + if let Some(("collect", _, _, span, _)) = method_call(recv) { unnecessary_join::check(cx, expr, recv, join_arg, span); } }, ("last", []) | ("skip", [_]) => { - if let Some((name2, recv2, args2, _span2)) = method_call(recv) { + if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } @@ -3604,13 +3605,13 @@ impl Methods { (name @ ("map" | "map_err"), [m_arg]) => { if name == "map" { map_clone::check(cx, expr, recv, m_arg, self.msrv); - if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _)) = method_call(recv) { + if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) { iter_kv_map::check(cx, map_name, expr, recv2, m_arg); } } else { map_err_ignore::check(cx, expr, m_arg); } - if let Some((name, recv2, args, span2)) = method_call(recv) { + if let Some((name, recv2, args, span2,_)) = method_call(recv) { match (name, args) { ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), @@ -3630,7 +3631,7 @@ impl Methods { manual_ok_or::check(cx, expr, recv, def, map); }, ("next", []) => { - if let Some((name2, recv2, args2, _)) = method_call(recv) { + if let Some((name2, recv2, args2, _, _)) = method_call(recv) { match (name2, args2) { ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), @@ -3643,10 +3644,10 @@ impl Methods { } }, ("nth", [n_arg]) => match method_call(recv) { - Some(("bytes", recv2, [], _)) => bytes_nth::check(cx, expr, recv2, n_arg), - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), - Some(("iter", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), - Some(("iter_mut", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), + Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), + Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), + Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), @@ -3711,7 +3712,7 @@ impl Methods { }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("take", [_arg]) => { - if let Some((name2, recv2, args2, _span2)) = method_call(recv) { + if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } @@ -3734,13 +3735,13 @@ impl Methods { }, ("unwrap", []) => { match method_call(recv) { - Some(("get", recv, [get_arg], _)) => { + Some(("get", recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, false); }, - Some(("get_mut", recv, [get_arg], _)) => { + Some(("get_mut", recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, true); }, - Some(("or", recv, [or_arg], or_span)) => { + Some(("or", recv, [or_arg], or_span, _)) => { or_then_unwrap::check(cx, expr, recv, or_arg, or_span); }, _ => {}, @@ -3749,19 +3750,19 @@ impl Methods { }, ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests), ("unwrap_or", [u_arg]) => match method_call(recv) { - Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _)) => { + Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => { manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, - Some(("map", m_recv, [m_arg], span)) => { + Some(("map", m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); }, - Some(("then_some", t_recv, [t_arg], _)) => { + Some(("then_some", t_recv, [t_arg], _, _)) => { obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg); }, _ => {}, }, ("unwrap_or_else", [u_arg]) => match method_call(recv) { - Some(("map", recv, [map_arg], _)) + Some(("map", recv, [map_arg], _, _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, _ => { unwrap_or_else_default::check(cx, expr, recv, u_arg); @@ -3782,7 +3783,7 @@ impl Methods { } fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { - if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span)) = method_call(recv) { + if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span, _)) = method_call(recv) { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); } } diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 66f9e28596e87..b088e642e0e9a 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -3,94 +3,99 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_move_expr_to_closure, is_trait_method, path_to_local, path_to_local_id, CaptureKind}; -use if_chain::if_chain; +use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection}; +use clippy_utils::{ + can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id, + CaptureKind, +}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind}; +use rustc_hir::{ + BindingAnnotation, Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind, +}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; -use rustc_span::Span; +use rustc_middle::ty::{self, AssocKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_span::symbol::Ident; +use rustc_span::{sym, Span, Symbol}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; -pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - 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(method, receiver, args, _) = expr.kind; - if let ExprKind::MethodCall(chain_method, ..) = receiver.kind; - if chain_method.ident.name == sym!(collect) && is_trait_method(cx, receiver, sym::Iterator); - then { - let ty = cx.typeck_results().expr_ty(receiver); - let mut applicability = Applicability::MaybeIncorrect; - let is_empty_sugg = "next().is_none()".to_string(); - let method_name = method.ident.name.as_str(); - let sugg = if is_type_diagnostic_item(cx, ty, sym::Vec) || - is_type_diagnostic_item(cx, ty, sym::VecDeque) || - is_type_diagnostic_item(cx, ty, sym::LinkedList) || - is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { - match method_name { - "len" => "count()".to_string(), - "is_empty" => is_empty_sugg, - "contains" => { - let contains_arg = snippet_with_applicability(cx, args[0].span, "??", &mut applicability); - let (arg, pred) = contains_arg - .strip_prefix('&') - .map_or(("&x", &*contains_arg), |s| ("x", s)); - format!("any(|{arg}| x == {pred})") - } - _ => return, - } - } - else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) || - is_type_diagnostic_item(cx, ty, sym::HashMap) { - match method_name { - "is_empty" => is_empty_sugg, - _ => return, - } - } - else { - return; - }; - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - chain_method.ident.span.with_hi(expr.span.hi()), - NEEDLESS_COLLECT_MSG, - "replace with", - sugg, - applicability, - ); - } - } -} +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + name_span: Span, + collect_expr: &'tcx Expr<'_>, + iter_expr: &'tcx Expr<'tcx>, + call_span: Span, +) { + if let Some(parent) = get_parent_node(cx.tcx, collect_expr.hir_id) { + match parent { + Node::Expr(parent) => { + if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind { + let mut app = Applicability::MachineApplicable; + let name = name.ident.as_str(); + let collect_ty = cx.typeck_results().expr_ty(collect_expr); -fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - if let ExprKind::Block(block, _) = expr.kind { - for stmt in block.stmts { - if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let PatKind::Binding(_, id, ..) = local.pat.kind; - if let Some(init_expr) = local.init; - if let ExprKind::MethodCall(method_name, iter_source, [], ..) = init_expr.kind; - if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); - let ty = cx.typeck_results().expr_ty(init_expr); - if is_type_diagnostic_item(cx, ty, sym::Vec) || - is_type_diagnostic_item(cx, ty, sym::VecDeque) || - is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || - is_type_diagnostic_item(cx, ty, sym::LinkedList); - let iter_ty = cx.typeck_results().expr_ty(iter_source); - if let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty)); - if let [iter_call] = &*iter_calls; - then { + let sugg: String = match name { + "len" => { + if let Some(adt) = collect_ty.ty_adt_def() + && matches!( + cx.tcx.get_diagnostic_name(adt.did()), + Some(sym::Vec | sym::VecDeque | sym::LinkedList | sym::BinaryHeap) + ) + { + "count()".into() + } else { + return; + } + }, + "is_empty" + if is_is_empty_sig(cx, parent.hir_id) + && iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) => + { + "next().is_none()".into() + }, + "contains" => { + if is_contains_sig(cx, parent.hir_id, iter_expr) + && let Some(arg) = args.first() + { + let (span, prefix) = if let ExprKind::AddrOf(_, _, arg) = arg.kind { + (arg.span, "") + } else { + (arg.span, "*") + }; + let snip = snippet_with_applicability(cx, span, "??", &mut app); + format!("any(|x| x == {prefix}{snip})") + } else { + return; + } + }, + _ => return, + }; + + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + call_span.with_hi(parent.span.hi()), + NEEDLESS_COLLECT_MSG, + "replace with", + sugg, + app, + ); + } + }, + Node::Local(l) => { + if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) + = l.pat.kind + && let ty = cx.typeck_results().expr_ty(collect_expr) + && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList].into_iter() + .any(|item| is_type_diagnostic_item(cx, ty, item)) + && let iter_ty = cx.typeck_results().expr_ty(iter_expr) + && let Some(block) = get_enclosing_block(cx, l.hir_id) + && let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty)) + && let [iter_call] = &*iter_calls + { let mut used_count_visitor = UsedCountVisitor { cx, id, @@ -102,20 +107,20 @@ 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(method_name.ident.span); + let mut span = MultiSpan::from_span(name_span); span.push_span_label(iter_call.span, "the iterator could be used here instead"); span_lint_hir_and_then( cx, super::NEEDLESS_COLLECT, - init_expr.hir_id, + collect_expr.hir_id, span, NEEDLESS_COLLECT_MSG, |diag| { - let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); + let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx)); diag.multipart_suggestion( iter_call.get_suggestion_text(), vec![ - (stmt.span, String::new()), + (l.span, String::new()), (iter_call.span, iter_replacement) ], Applicability::MaybeIncorrect, @@ -123,11 +128,61 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo }, ); } - } + }, + _ => (), } } } +/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool` +fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool { + cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| { + let sig = cx.tcx.fn_sig(id).skip_binder(); + sig.inputs().len() == 1 && sig.output().is_bool() + }) +} + +/// Checks if `::Item` is the same as `::Item` +fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool { + let item = Symbol::intern("Item"); + if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator) + && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, item, [iter_ty]) + && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty]) + && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions( + cx.param_env, + cx.tcx.mk_projection(into_iter_item_proj.item_def_id, into_iter_item_proj.substs) + ) + { + iter_item_ty == into_iter_item_ty + } else { + false + } +} + +/// Checks if the given method call matches the expected signature of +/// `([&[mut]] self, &::Item) -> bool` +fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -> bool { + let typeck = cx.typeck_results(); + if let Some(id) = typeck.type_dependent_def_id(call_id) + && let sig = cx.tcx.fn_sig(id) + && sig.skip_binder().output().is_bool() + && let [_, search_ty] = *sig.skip_binder().inputs() + && let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind() + && let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(iter_item) = cx.tcx + .associated_items(iter_trait) + .find_by_name_and_kind(cx.tcx, Ident::with_dummy_span(Symbol::intern("Item")), AssocKind::Type, iter_trait) + && let substs = cx.tcx.mk_substs([GenericArg::from(typeck.expr_ty_adjusted(iter_expr))].into_iter()) + && let proj_ty = cx.tcx.mk_projection(iter_item.def_id, substs) + && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty) + { + item_ty == EarlyBinder(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id)) + } else { + false + } +} + struct IterFunction { func: IterFunctionKind, span: Span, diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 2dd8c826ab382..a022aac4bfee5 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,8 +13,9 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy, - Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, + self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericParamDefKind, IntTy, ParamEnv, Predicate, + PredicateKind, ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitor, UintTy, VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; @@ -938,3 +939,132 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { (Err(_), _) => 0, } } + +/// Makes the projection type for the named associated type in the given impl or trait impl. +/// +/// This function is for associated types which are "known" to exist, and as such, will only return +/// `None` when debug assertions are disabled in order to prevent ICE's. With debug assertions +/// enabled this will check that the named associated type exists, the correct number of +/// substitutions are given, and that the correct kinds of substitutions are given (lifetime, +/// constant or type). This will not check if type normalization would succeed. +pub fn make_projection<'tcx>( + tcx: TyCtxt<'tcx>, + container_id: DefId, + assoc_ty: Symbol, + substs: impl IntoIterator>>, +) -> Option> { + fn helper<'tcx>( + tcx: TyCtxt<'tcx>, + container_id: DefId, + assoc_ty: Symbol, + substs: SubstsRef<'tcx>, + ) -> Option> { + let Some(assoc_item) = tcx + .associated_items(container_id) + .find_by_name_and_kind(tcx, Ident::with_dummy_span(assoc_ty), AssocKind::Type, container_id) + else { + debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`"); + return None; + }; + #[cfg(debug_assertions)] + { + let generics = tcx.generics_of(assoc_item.def_id); + let generic_count = generics.parent_count + generics.params.len(); + let params = generics + .parent + .map_or([].as_slice(), |id| &*tcx.generics_of(id).params) + .iter() + .chain(&generics.params) + .map(|x| &x.kind); + + debug_assert!( + generic_count == substs.len(), + "wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\ + note: the expected parameters are: {:#?}\n\ + the given arguments are: `{:#?}`", + assoc_item.def_id, + substs.len(), + generic_count, + params.map(GenericParamDefKind::descr).collect::>(), + substs, + ); + + if let Some((idx, (param, arg))) = params + .clone() + .zip(substs.iter().map(GenericArg::unpack)) + .enumerate() + .find(|(_, (param, arg))| { + !matches!( + (param, arg), + (GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) + | (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) + | (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) + ) + }) + { + debug_assert!( + false, + "mismatched subst type at index {}: expected a {}, found `{:?}`\n\ + note: the expected parameters are {:#?}\n\ + the given arguments are {:#?}", + idx, + param.descr(), + arg, + params.map(GenericParamDefKind::descr).collect::>(), + substs, + ); + } + } + + Some(ProjectionTy { + substs, + item_def_id: assoc_item.def_id, + }) + } + helper( + tcx, + container_id, + assoc_ty, + tcx.mk_substs(substs.into_iter().map(Into::into)), + ) +} + +/// Normalizes the named associated type in the given impl or trait impl. +/// +/// This function is for associated types which are "known" to be valid with the given +/// substitutions, and as such, will only return `None` when debug assertions are disabled in order +/// to prevent ICE's. With debug assertions enabled this will check that that type normalization +/// succeeds as well as everything checked by `make_projection`. +pub fn make_normalized_projection<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + container_id: DefId, + assoc_ty: Symbol, + substs: impl IntoIterator>>, +) -> Option> { + fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: ProjectionTy<'tcx>) -> Option> { + #[cfg(debug_assertions)] + if let Some((i, subst)) = ty + .substs + .iter() + .enumerate() + .find(|(_, subst)| subst.has_late_bound_regions()) + { + debug_assert!( + false, + "substs contain late-bound region at index `{i}` which can't be normalized.\n\ + use `TyCtxt::erase_late_bound_regions`\n\ + note: subst is `{subst:#?}`", + ); + return None; + } + match tcx.try_normalize_erasing_regions(param_env, tcx.mk_projection(ty.item_def_id, ty.substs)) { + Ok(ty) => Some(ty), + Err(e) => { + debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); + None + }, + } + } + helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?) +} diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index 6ecbbcb624955..2659ad384885e 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -33,4 +33,33 @@ fn main() { // `BinaryHeap` doesn't have `contains` method sample.iter().count(); sample.iter().next().is_none(); + + // Don't lint string from str + let _ = ["", ""].into_iter().collect::().is_empty(); + + let _ = sample.iter().next().is_none(); + let _ = sample.iter().any(|x| x == &0); + + struct VecWrapper(Vec); + impl core::ops::Deref for VecWrapper { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl IntoIterator for VecWrapper { + type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } + } + impl FromIterator for VecWrapper { + fn from_iter>(iter: I) -> Self { + Self(Vec::from_iter(iter)) + } + } + + let _ = sample.iter().next().is_none(); + let _ = sample.iter().any(|x| x == &0); } diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 8dc69bcf5b38d..535ec82982b13 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -33,4 +33,33 @@ fn main() { // `BinaryHeap` doesn't have `contains` method sample.iter().collect::>().len(); sample.iter().collect::>().is_empty(); + + // Don't lint string from str + let _ = ["", ""].into_iter().collect::().is_empty(); + + let _ = sample.iter().collect::>().is_empty(); + let _ = sample.iter().collect::>().contains(&&0); + + struct VecWrapper(Vec); + impl core::ops::Deref for VecWrapper { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl IntoIterator for VecWrapper { + type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } + } + impl FromIterator for VecWrapper { + fn from_iter>(iter: I) -> Self { + Self(Vec::from_iter(iter)) + } + } + + let _ = sample.iter().collect::>().is_empty(); + let _ = sample.iter().collect::>().contains(&&0); } diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 039091627a8d6..584d2a1d8356f 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -66,5 +66,29 @@ error: avoid using `collect()` when not needed LL | sample.iter().collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` -error: aborting due to 11 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:40:27 + | +LL | let _ = sample.iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:41:27 + | +LL | let _ = sample.iter().collect::>().contains(&&0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:63:27 + | +LL | let _ = sample.iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:64:27 + | +LL | let _ = sample.iter().collect::>().contains(&&0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` + +error: aborting due to 15 previous errors From 5b1e445b9abff4f9eec0c5911389913108f7ba93 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 7 Nov 2022 14:08:40 -0500 Subject: [PATCH 105/169] Don't lint `explicit_auto_deref` when the target type is a projection containing a generic argument --- clippy_lints/src/dereference.rs | 1 + tests/ui/explicit_auto_deref.fixed | 11 +++++++++++ tests/ui/explicit_auto_deref.rs | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 86e7bc2e6d197..e2624ee80c0fa 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1362,6 +1362,7 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc continue; }, ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty), + ty::Projection(_) if ty.has_non_region_param() => TyPosition::new_deref_stable_for_result(precedence, ty), ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => { Position::ReborrowStable(precedence).into() }, diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed index d1d35e5c0eb46..59ff5e4040a3d 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -266,4 +266,15 @@ fn main() { } x }; + + trait WithAssoc { + type Assoc: ?Sized; + } + impl WithAssoc for String { + type Assoc = str; + } + fn takes_assoc(_: &T::Assoc) -> T { + unimplemented!() + } + let _: String = takes_assoc(&*String::new()); } diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index deedafad153b9..bcfb60c327886 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -266,4 +266,15 @@ fn main() { } *x }; + + trait WithAssoc { + type Assoc: ?Sized; + } + impl WithAssoc for String { + type Assoc = str; + } + fn takes_assoc(_: &T::Assoc) -> T { + unimplemented!() + } + let _: String = takes_assoc(&*String::new()); } From 3d4b73c26da1630318e76f43a7dedfc4dbc716cd Mon Sep 17 00:00:00 2001 From: kraktus Date: Mon, 7 Nov 2022 21:00:37 +0100 Subject: [PATCH 106/169] [`excessive_bools`] lint trait functions even without bodies --- clippy_lints/src/excessive_bools.rs | 14 ++++++++++--- tests/ui/fn_params_excessive_bools.rs | 2 ++ tests/ui/fn_params_excessive_bools.stderr | 24 +++++++++++++++++++---- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 68c5e5ab1f1eb..fc2912f696e03 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, Ty}; +use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -114,7 +114,7 @@ impl ExcessiveBools { } fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) { - if self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) { + if !span.from_expansion() && self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) { span_lint_and_help( cx, FN_PARAMS_EXCESSIVE_BOOLS, @@ -152,6 +152,15 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { } } + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'tcx>) { + // functions with a body are already checked by `check_fn` + if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind + && fn_sig.header.abi == Abi::Rust + { + self.check_fn_sig(cx, fn_sig.decl, fn_sig.span); + } + } + fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -163,7 +172,6 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { ) { if let Some(fn_header) = fn_kind.header() && fn_header.abi == Abi::Rust - && !span.from_expansion() && get_parent_as_impl(cx.tcx, hir_id) .map_or(true, |impl_item| impl_item.of_trait.is_none() diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs index 7995912110d6e..f53e531629aae 100644 --- a/tests/ui/fn_params_excessive_bools.rs +++ b/tests/ui/fn_params_excessive_bools.rs @@ -23,10 +23,12 @@ fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} struct S; trait Trait { + // should warn for trait functions with and without body fn f(_: bool, _: bool, _: bool, _: bool); fn g(_: bool, _: bool, _: bool, _: Vec); #[allow(clippy::fn_params_excessive_bools)] fn h(_: bool, _: bool, _: bool, _: bool, _: bool, _: bool); + fn i(_: bool, _: bool, _: bool, _: bool) {} } impl S { diff --git a/tests/ui/fn_params_excessive_bools.stderr b/tests/ui/fn_params_excessive_bools.stderr index 8c2383873fe9d..43363b46972c3 100644 --- a/tests/ui/fn_params_excessive_bools.stderr +++ b/tests/ui/fn_params_excessive_bools.stderr @@ -16,7 +16,23 @@ LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:33:5 + --> $DIR/fn_params_excessive_bools.rs:27:5 + | +LL | fn f(_: bool, _: bool, _: bool, _: bool); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:31:5 + | +LL | fn i(_: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:35:5 | LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +40,7 @@ LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:48:5 + --> $DIR/fn_params_excessive_bools.rs:50:5 | LL | / fn n(_: bool, _: u32, _: bool, _: Box, _: bool, _: bool) { LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {} @@ -34,12 +50,12 @@ LL | | } = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:49:9 + --> $DIR/fn_params_excessive_bools.rs:51:9 | LL | fn nn(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider refactoring bools into two-variant enums -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors From 80e5856b02060f1185b5976b515eeada18811756 Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Sun, 16 Oct 2022 11:37:26 -0700 Subject: [PATCH 107/169] `result_large_err` show largest variants in err msg --- clippy_lints/src/functions/result.rs | 68 ++++++++++++++++++++------ clippy_lints/src/large_enum_variant.rs | 60 ++++------------------- clippy_utils/src/ty.rs | 42 ++++++++++++++-- tests/ui/result_large_err.rs | 12 +++++ tests/ui/result_large_err.stderr | 30 +++++++++--- 5 files changed, 138 insertions(+), 74 deletions(-) diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 113c4e9f50910..3e288467ba168 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -2,12 +2,12 @@ use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Adt, Ty}; use rustc_span::{sym, Span}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::trait_ref_of_method; -use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item}; +use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item, AdtVariantInfo}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; @@ -84,17 +84,57 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S } fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) { - let ty_size = approx_ty_size(cx, err_ty); - if ty_size >= large_err_threshold { - span_lint_and_then( - cx, - RESULT_LARGE_ERR, - hir_ty_span, - "the `Err`-variant returned from this function is very large", - |diag: &mut Diagnostic| { - diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); - diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); - }, - ); + if_chain! { + if let Adt(adt, subst) = err_ty.kind(); + if let Some(local_def_id) = err_ty.ty_adt_def().expect("already checked this is adt").did().as_local(); + if let Some(hir::Node::Item(item)) = cx + .tcx + .hir() + .find_by_def_id(local_def_id); + if let hir::ItemKind::Enum(ref def, _) = item.kind; + then { + let variants_size = AdtVariantInfo::new(cx, *adt, subst); + if variants_size[0].size >= large_err_threshold { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag| { + diag.span_label( + def.variants[variants_size[0].ind].span, + format!("the largest variant contains at least {} bytes", variants_size[0].size), + ); + + for variant in &variants_size[1..] { + if variant.size >= large_err_threshold { + let variant_def = &def.variants[variant.ind]; + diag.span_label( + variant_def.span, + format!("the variant `{}` contains at least {} bytes", variant_def.ident, variant.size), + ); + } + } + + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + } + ); + } + } + else { + let ty_size = approx_ty_size(cx, err_ty); + if ty_size >= large_err_threshold { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag: &mut Diagnostic| { + diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + }, + ); + } + } } } diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 8ed7e4bb196cd..fd82d9f80f973 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,12 +1,15 @@ //! lint when there is a large size difference between variants on an enum use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy}; +use clippy_utils::{ + diagnostics::span_lint_and_then, + ty::{approx_ty_size, is_copy, AdtVariantInfo}, +}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty}; +use rustc_middle::ty::{Adt, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -72,49 +75,6 @@ impl LargeEnumVariant { } } -struct FieldInfo { - ind: usize, - size: u64, -} - -struct VariantInfo { - ind: usize, - size: u64, - fields_size: Vec, -} - -fn variants_size<'tcx>( - cx: &LateContext<'tcx>, - adt: AdtDef<'tcx>, - subst: &'tcx List>, -) -> Vec { - let mut variants_size = adt - .variants() - .iter() - .enumerate() - .map(|(i, variant)| { - let mut fields_size = variant - .fields - .iter() - .enumerate() - .map(|(i, f)| FieldInfo { - ind: i, - size: approx_ty_size(cx, f.ty(cx.tcx, subst)), - }) - .collect::>(); - fields_size.sort_by(|a, b| (a.size.cmp(&b.size))); - - VariantInfo { - ind: i, - size: fields_size.iter().map(|info| info.size).sum(), - fields_size, - } - }) - .collect::>(); - variants_size.sort_by(|a, b| (b.size.cmp(&a.size))); - variants_size -} - impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { @@ -130,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { if adt.variants().len() <= 1 { return; } - let variants_size = variants_size(cx, *adt, subst); + let variants_size = AdtVariantInfo::new(cx, *adt, subst); let mut difference = variants_size[0].size - variants_size[1].size; if difference > self.maximum_size_difference_allowed { @@ -173,16 +133,16 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { .fields_size .iter() .rev() - .map_while(|val| { + .map_while(|&(ind, size)| { if difference > self.maximum_size_difference_allowed { - difference = difference.saturating_sub(val.size); + difference = difference.saturating_sub(size); Some(( - fields[val.ind].ty.span, + fields[ind].ty.span, format!( "Box<{}>", snippet_with_applicability( cx, - fields[val.ind].ty.span, + fields[ind].ty.span, "..", &mut applicability ) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a022aac4bfee5..3e9c605e3f953 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,9 +13,9 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericParamDefKind, IntTy, ParamEnv, Predicate, - PredicateKind, ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitor, UintTy, VariantDef, VariantDiscr, + self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericParamDefKind, IntTy, List, ParamEnv, + Predicate, PredicateKind, ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, + TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; @@ -845,6 +845,42 @@ pub fn for_each_top_level_late_bound_region( ty.visit_with(&mut V { index: 0, f }) } +pub struct AdtVariantInfo { + pub ind: usize, + pub size: u64, + + /// (ind, size) + pub fields_size: Vec<(usize, u64)>, +} + +impl AdtVariantInfo { + /// Returns ADT variants ordered by size + pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: &'tcx List>) -> Vec { + let mut variants_size = adt + .variants() + .iter() + .enumerate() + .map(|(i, variant)| { + let mut fields_size = variant + .fields + .iter() + .enumerate() + .map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst)))) + .collect::>(); + fields_size.sort_by(|(_, a_size), (_, b_size)| (a_size.cmp(b_size))); + + Self { + ind: i, + size: fields_size.iter().map(|(_, size)| size).sum(), + fields_size, + } + }) + .collect::>(); + variants_size.sort_by(|a, b| (b.size.cmp(&a.size))); + variants_size + } +} + /// Gets the struct or enum variant from the given `Res` pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> { match res { diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs index f7df3b8565501..9dd27d6dc01aa 100644 --- a/tests/ui/result_large_err.rs +++ b/tests/ui/result_large_err.rs @@ -50,6 +50,18 @@ impl LargeErrorVariants<()> { } } +enum MultipleLargeVariants { + _Biggest([u8; 1024]), + _AlsoBig([u8; 512]), + _Ok(usize), +} + +impl MultipleLargeVariants { + fn large_enum_error() -> Result<(), Self> { + Ok(()) + } +} + trait TraitForcesLargeError { fn large_error() -> Result<(), [u8; 512]> { Ok(()) diff --git a/tests/ui/result_large_err.stderr b/tests/ui/result_large_err.stderr index bea101fe20bf2..c386edfd21571 100644 --- a/tests/ui/result_large_err.stderr +++ b/tests/ui/result_large_err.stderr @@ -42,13 +42,29 @@ LL | pub fn param_large_error() -> Result<(), (u128, R, FullyDefinedLargeErro error: the `Err`-variant returned from this function is very large --> $DIR/result_large_err.rs:48:34 | +LL | _Omg([u8; 512]), + | --------------- the largest variant contains at least 512 bytes +... LL | pub fn large_enum_error() -> Result<(), Self> { - | ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 513 bytes + | ^^^^^^^^^^^^^^^^ | = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:54:25 + --> $DIR/result_large_err.rs:60:30 + | +LL | _Biggest([u8; 1024]), + | -------------------- the largest variant contains at least 1024 bytes +LL | _AlsoBig([u8; 512]), + | ------------------- the variant `_AlsoBig` contains at least 512 bytes +... +LL | fn large_enum_error() -> Result<(), Self> { + | ^^^^^^^^^^^^^^^^ + | + = help: try reducing the size of `MultipleLargeVariants`, for example by boxing large elements or replacing it with `Box` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:66:25 | LL | fn large_error() -> Result<(), [u8; 512]> { | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -56,7 +72,7 @@ LL | fn large_error() -> Result<(), [u8; 512]> { = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:73:29 + --> $DIR/result_large_err.rs:85:29 | LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -64,7 +80,7 @@ LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:82:40 + --> $DIR/result_large_err.rs:94:40 | LL | pub fn param_large_union() -> Result<(), UnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -72,7 +88,7 @@ LL | pub fn param_large_union() -> Result<(), UnionError> { = help: try reducing the size of `UnionError`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:91:34 + --> $DIR/result_large_err.rs:103:34 | LL | pub fn array_error_subst() -> Result<(), ArrayError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes @@ -80,12 +96,12 @@ LL | pub fn array_error_subst() -> Result<(), ArrayError> { = help: try reducing the size of `ArrayError`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:95:31 + --> $DIR/result_large_err.rs:107:31 | LL | pub fn array_error() -> Result<(), ArrayError<(i32, T), U>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes | = help: try reducing the size of `ArrayError<(i32, T), U>`, for example by boxing large elements or replacing it with `Box>` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors From 70010850e498de0b9b2a0b1874ab8271bb7da180 Mon Sep 17 00:00:00 2001 From: Grachev Mikhail Date: Tue, 8 Nov 2022 15:33:50 +0300 Subject: [PATCH 108/169] Update lint example for `collapsible_str_replace` --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1c0bbe086f7c8..686930a0bbe96 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -159,7 +159,7 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l"); + /// let hello = "hesuo worpd".replace(['s', 'u', 'p'], "l"); /// ``` #[clippy::version = "1.65.0"] pub COLLAPSIBLE_STR_REPLACE, From 43a6d0b39ce5403c12264065a1619b49a1670a94 Mon Sep 17 00:00:00 2001 From: Ryan Scheidter Date: Tue, 8 Nov 2022 07:48:39 -0600 Subject: [PATCH 109/169] Fixed Typo --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1c0bbe086f7c8..0a240bbee6cf6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1731,7 +1731,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). + /// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases (such as String::as_str). /// /// ### Why is this bad? /// Readability, this can be written more concisely as From 84c3a959a7579d368f235a22f0d626980aa693c2 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 9 Nov 2022 19:23:23 +0800 Subject: [PATCH 110/169] bless clippy --- tests/ui/crashes/ice-6250.stderr | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/ui/crashes/ice-6250.stderr b/tests/ui/crashes/ice-6250.stderr index 878897c410cf5..4506d1550bd4b 100644 --- a/tests/ui/crashes/ice-6250.stderr +++ b/tests/ui/crashes/ice-6250.stderr @@ -23,6 +23,11 @@ error[E0308]: mismatched types | LL | Some(reference) = cache.data.get(key) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: consider adding `let` + | +LL | let Some(reference) = cache.data.get(key) { + | +++ error: aborting due to 3 previous errors From 146bd1e13d910bc90c5316f11ddecd35f2673d65 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 9 Nov 2022 16:54:10 +0100 Subject: [PATCH 111/169] Add `unnecessary_safety_doc` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/doc.rs | 94 +++++++++++---- tests/ui/auxiliary/doc_unsafe_macros.rs | 8 ++ tests/ui/doc_unnecessary_unsafe.rs | 148 ++++++++++++++++++++++++ tests/ui/doc_unnecessary_unsafe.stderr | 51 ++++++++ 6 files changed, 278 insertions(+), 25 deletions(-) create mode 100644 tests/ui/doc_unnecessary_unsafe.rs create mode 100644 tests/ui/doc_unnecessary_unsafe.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bd67617f5bfa..f1697312d5c66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4449,6 +4449,7 @@ Released 2018-09-13 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings +[`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 98265410e8490..0ff039a4cdfd6 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -127,6 +127,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::doc::MISSING_PANICS_DOC_INFO, crate::doc::MISSING_SAFETY_DOC_INFO, crate::doc::NEEDLESS_DOCTEST_MAIN_INFO, + crate::doc::UNNECESSARY_SAFETY_DOC_INFO, crate::double_parens::DOUBLE_PARENS_INFO, crate::drop_forget_ref::DROP_COPY_INFO, crate::drop_forget_ref::DROP_NON_DROP_INFO, diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index fbc00836b390f..2ff58ade31fa4 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -221,6 +221,42 @@ declare_clippy_lint! { "possible typo for an intra-doc link" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the doc comments of publicly visible + /// safe functions and traits and warns if there is a `# Safety` section. + /// + /// ### Why is this bad? + /// Safe functions and traits are safe to implement and therefore do not + /// need to describe safety preconditions that users are required to uphold. + /// + /// ### Examples + /// ```rust + ///# type Universe = (); + /// /// # Safety + /// /// + /// /// This function should not be called before the horsemen are ready. + /// pub fn start_apocalypse_but_safely(u: &mut Universe) { + /// unimplemented!(); + /// } + /// ``` + /// + /// The function is safe, so there shouldn't be any preconditions + /// that have to be explained for safety reasons. + /// + /// ```rust + ///# type Universe = (); + /// /// This function should really be documented + /// pub fn start_apocalypse(u: &mut Universe) { + /// unimplemented!(); + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub UNNECESSARY_SAFETY_DOC, + style, + "`pub fn` or `pub trait` with `# Safety` docs" +} + #[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct DocMarkdown { @@ -243,7 +279,8 @@ impl_lint_pass!(DocMarkdown => [ MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, - NEEDLESS_DOCTEST_MAIN + NEEDLESS_DOCTEST_MAIN, + UNNECESSARY_SAFETY_DOC, ]); impl<'tcx> LateLintPass<'tcx> for DocMarkdown { @@ -254,7 +291,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; match item.kind { hir::ItemKind::Fn(ref sig, _, body_id) => { if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { @@ -271,15 +308,20 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { hir::ItemKind::Impl(impl_) => { self.in_trait_impl = impl_.of_trait.is_some(); }, - hir::ItemKind::Trait(_, unsafety, ..) => { - if !headers.safety && unsafety == hir::Unsafety::Unsafe { - span_lint( - cx, - MISSING_SAFETY_DOC, - cx.tcx.def_span(item.def_id), - "docs for unsafe trait missing `# Safety` section", - ); - } + hir::ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) { + (false, hir::Unsafety::Unsafe) => span_lint( + cx, + MISSING_SAFETY_DOC, + cx.tcx.def_span(item.def_id), + "docs for unsafe trait missing `# Safety` section", + ), + (true, hir::Unsafety::Normal) => span_lint( + cx, + UNNECESSARY_SAFETY_DOC, + cx.tcx.def_span(item.def_id), + "docs for safe trait have unnecessary `# Safety` section", + ), + _ => (), }, _ => (), } @@ -293,7 +335,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { if !in_external_macro(cx.tcx.sess, item.span) { lint_for_missing_headers(cx, item.def_id.def_id, sig, headers, None, None); @@ -303,7 +345,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) { return; } @@ -343,14 +385,20 @@ fn lint_for_missing_headers( } let span = cx.tcx.def_span(def_id); - - if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe { - span_lint( + match (headers.safety, sig.header.unsafety) { + (false, hir::Unsafety::Unsafe) => span_lint( cx, MISSING_SAFETY_DOC, span, "unsafe function's docs miss `# Safety` section", - ); + ), + (true, hir::Unsafety::Normal) => span_lint( + cx, + UNNECESSARY_SAFETY_DOC, + span, + "safe function's docs have unnecessary `# Safety` section", + ), + _ => (), } if !headers.panics && panic_span.is_some() { span_lint_and_note( @@ -452,7 +500,7 @@ struct DocHeaders { panics: bool, } -fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[Attribute]) -> DocHeaders { +fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[Attribute]) -> Option { use pulldown_cmark::{BrokenLink, CowStr, Options}; /// We don't want the parser to choke on intra doc links. Since we don't /// actually care about rendering them, just pretend that all broken links are @@ -473,11 +521,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ } else if attr.has_name(sym::doc) { // ignore mix of sugared and non-sugared doc // don't trigger the safety or errors check - return DocHeaders { - safety: true, - errors: true, - panics: true, - }; + return None; } } @@ -489,7 +533,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ } if doc.is_empty() { - return DocHeaders::default(); + return Some(DocHeaders::default()); } let mut cb = fake_broken_link_callback; @@ -512,7 +556,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ (previous, current) => Err(((previous, previous_range), (current, current_range))), } }); - check_doc(cx, valid_idents, events, &spans) + Some(check_doc(cx, valid_idents, events, &spans)) } const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; diff --git a/tests/ui/auxiliary/doc_unsafe_macros.rs b/tests/ui/auxiliary/doc_unsafe_macros.rs index 869672d1eda5e..3d917e3dc75e9 100644 --- a/tests/ui/auxiliary/doc_unsafe_macros.rs +++ b/tests/ui/auxiliary/doc_unsafe_macros.rs @@ -6,3 +6,11 @@ macro_rules! undocd_unsafe { } }; } +#[macro_export] +macro_rules! undocd_safe { + () => { + pub fn vey_oy() { + unimplemented!(); + } + }; +} diff --git a/tests/ui/doc_unnecessary_unsafe.rs b/tests/ui/doc_unnecessary_unsafe.rs new file mode 100644 index 0000000000000..d9e9363b0f4bd --- /dev/null +++ b/tests/ui/doc_unnecessary_unsafe.rs @@ -0,0 +1,148 @@ +// aux-build:doc_unsafe_macros.rs + +#![allow(clippy::let_unit_value)] + +#[macro_use] +extern crate doc_unsafe_macros; + +/// This is has no safety section, and does not need one either +pub fn destroy_the_planet() { + unimplemented!(); +} + +/// This one does not need a `Safety` section +/// +/// # Safety +/// +/// This function shouldn't be called unless the horsemen are ready +pub fn apocalypse(universe: &mut ()) { + unimplemented!(); +} + +/// This is a private function, skip to match behavior with `missing_safety_doc`. +/// +/// # Safety +/// +/// Boo! +fn you_dont_see_me() { + unimplemented!(); +} + +mod private_mod { + /// This is public but unexported function, skip to match behavior with `missing_safety_doc`. + /// + /// # Safety + /// + /// Very safe! + pub fn only_crate_wide_accessible() { + unimplemented!(); + } + + /// # Safety + /// + /// Unnecessary safety! + pub fn republished() { + unimplemented!(); + } +} + +pub use private_mod::republished; + +pub trait SafeTraitSafeMethods { + fn woefully_underdocumented(self); + + /// # Safety + /// + /// Unnecessary! + fn documented(self); +} + +pub trait SafeTrait { + fn method(); +} + +/// # Safety +/// +/// Unnecessary! +pub trait DocumentedSafeTrait { + fn method2(); +} + +pub struct Struct; + +impl SafeTraitSafeMethods for Struct { + fn woefully_underdocumented(self) { + // all is well + } + + fn documented(self) { + // all is still well + } +} + +impl SafeTrait for Struct { + fn method() {} +} + +impl DocumentedSafeTrait for Struct { + fn method2() {} +} + +impl Struct { + /// # Safety + /// + /// Unnecessary! + pub fn documented() -> Self { + unimplemented!(); + } + + pub fn undocumented(&self) { + unimplemented!(); + } + + /// Private, fine again to stay consistent with `missing_safety_doc`. + /// + /// # Safety + /// + /// Unnecessary! + fn private(&self) { + unimplemented!(); + } +} + +macro_rules! very_safe { + () => { + pub fn whee() { + unimplemented!() + } + + /// # Safety + /// + /// Driving is very safe already! + pub fn drive() { + whee() + } + }; +} + +very_safe!(); + +// we don't lint code from external macros +undocd_safe!(); + +fn main() {} + +// do not lint if any parent has `#[doc(hidden)]` attribute +// see #7347 +#[doc(hidden)] +pub mod __macro { + pub struct T; + impl T { + pub unsafe fn f() {} + } +} + +/// # Implementation safety +pub trait DocumentedSafeTraitWithImplementationHeader { + fn method(); +} diff --git a/tests/ui/doc_unnecessary_unsafe.stderr b/tests/ui/doc_unnecessary_unsafe.stderr new file mode 100644 index 0000000000000..83b2efbb346be --- /dev/null +++ b/tests/ui/doc_unnecessary_unsafe.stderr @@ -0,0 +1,51 @@ +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:18:1 + | +LL | pub fn apocalypse(universe: &mut ()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-safety-doc` implied by `-D warnings` + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:44:5 + | +LL | pub fn republished() { + | ^^^^^^^^^^^^^^^^^^^^ + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:57:5 + | +LL | fn documented(self); + | ^^^^^^^^^^^^^^^^^^^^ + +error: docs for safe trait have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:67:1 + | +LL | pub trait DocumentedSafeTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:95:5 + | +LL | pub fn documented() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:122:9 + | +LL | pub fn drive() { + | ^^^^^^^^^^^^^^ +... +LL | very_safe!(); + | ------------ in this macro invocation + | + = note: this error originates in the macro `very_safe` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: docs for safe trait have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:146:1 + | +LL | pub trait DocumentedSafeTraitWithImplementationHeader { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + From 708c2d95c14829e0acbe7f40d9bea39e7bb03450 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Sat, 1 Oct 2022 15:58:10 +0200 Subject: [PATCH 112/169] feat: add and implement unchecked_duration_subtraction lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 + .../src/unchecked_duration_subtraction.rs | 107 ++++++++++++++++++ clippy_lints/src/utils/conf.rs | 2 +- 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/unchecked_duration_subtraction.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bd67617f5bfa..eb4ca9801432c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4428,6 +4428,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 +[`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a4bacb780349b..a5407b65b88d1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -279,6 +279,7 @@ mod trailing_empty_array; mod trait_bounds; mod transmute; mod types; +mod unchecked_duration_subtraction; mod undocumented_unsafe_blocks; mod unicode; mod uninit_vec; @@ -921,6 +922,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv))); + store.register_late_pass(move || Box::new(unchecked_duration_subtraction::UncheckedDurationSubtraction::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unchecked_duration_subtraction.rs b/clippy_lints/src/unchecked_duration_subtraction.rs new file mode 100644 index 0000000000000..fb08067de8d4f --- /dev/null +++ b/clippy_lints/src/unchecked_duration_subtraction.rs @@ -0,0 +1,107 @@ +use clippy_utils::{diagnostics, meets_msrv, msrvs, source, ty}; +use rustc_errors::Applicability; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::sym; + +declare_clippy_lint! { + /// ### What it does + /// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`]. + /// + /// ### Why is this bad? + /// Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or + /// unintentional panics. + /// + /// ### Example + /// ```rust + /// let time_passed = Instant::now() - Duration::from_secs(5); + /// ``` + /// + /// Use instead: + /// ```rust + /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); + /// ``` + /// + /// [`Duration`]: std::time::Duration + /// [`Instant::now()`]: std::time::Instant::now; + #[clippy::version = "1.65.0"] + pub UNCHECKED_DURATION_SUBTRACTION, + suspicious, + "finds unchecked subtraction of a 'Duration' from an 'Instant'" +} + +pub struct UncheckedDurationSubtraction { + msrv: Option, +} + +impl UncheckedDurationSubtraction { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(UncheckedDurationSubtraction => [UNCHECKED_DURATION_SUBTRACTION]); + +impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) { + return; + } + + if_chain! { + if let ExprKind::Binary(op, lhs, rhs) = expr.kind; + + if let BinOpKind::Sub = op.node; + + // get types of left and right side + if is_an_instant(cx, lhs); + if is_a_duration(cx, rhs); + + then { + print_lint_and_sugg(cx, lhs, rhs, expr) + } + } + } +} + +fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + + match expr_ty.kind() { + rustc_middle::ty::Adt(def, _) => { + clippy_utils::match_def_path(cx, dbg!(def).did(), &clippy_utils::paths::INSTANT) + }, + _ => false, + } +} + +fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration) +} + +fn print_lint_and_sugg(cx: &LateContext<'_>, left_expr: &Expr<'_>, right_expr: &Expr<'_>, expr: &Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + + let left_expr = + source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability); + let right_expr = source::snippet_with_applicability( + cx, + right_expr.span, + "std::time::Duration::from_secs(1)", + &mut applicability, + ); + + diagnostics::span_lint_and_sugg( + cx, + UNCHECKED_DURATION_SUBTRACTION, + expr.span, + "unchecked subtraction of a 'Duration' from an 'Instant'", + "try", + format!("{}.checked_sub({}).unwrap();", left_expr, right_expr), + applicability, + ); +} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 0cf49ca6d429b..ab58e9b8b68d3 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -213,7 +213,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION. /// /// The minimum rust version that the project supports (msrv: Option = None), From 3b4e42b91b0df11c43983c6fe67bb971f472aa3f Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Sat, 1 Oct 2022 16:02:57 +0200 Subject: [PATCH 113/169] test: add tests for 'unchecked_duration_subtraction' lint --- tests/ui/unchecked_duration_subtraction.rs | 16 ++++++++++ .../ui/unchecked_duration_subtraction.stderr | 32 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/ui/unchecked_duration_subtraction.rs create mode 100644 tests/ui/unchecked_duration_subtraction.stderr diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_duration_subtraction.rs new file mode 100644 index 0000000000000..fff1d13720d98 --- /dev/null +++ b/tests/ui/unchecked_duration_subtraction.rs @@ -0,0 +1,16 @@ +#![warn(clippy::unchecked_duration_subtraction)] + +use std::time::{Duration, Instant}; + +fn main() { + let _first = Instant::now(); + let second = Duration::from_secs(3); + + let _ = _first - second; + + let _ = Instant::now() - Duration::from_secs(5); + + let _ = _first - Duration::from_secs(5); + + let _ = Instant::now() - second; +} diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr new file mode 100644 index 0000000000000..827b18a5a0972 --- /dev/null +++ b/tests/ui/unchecked_duration_subtraction.stderr @@ -0,0 +1,32 @@ +[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:9:13 + | +LL | let _ = _first - second; + | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap();` + | + = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` + +[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:11:13 + | +LL | let _ = Instant::now() - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap();` + +[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:13:13 + | +LL | let _ = _first - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap();` + +[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:15:13 + | +LL | let _ = Instant::now() - second; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap();` + +error: aborting due to 4 previous errors + From b280dbe5f73286a3f2a02e93c6c3e42181ec8aca Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Sat, 1 Oct 2022 16:50:45 +0200 Subject: [PATCH 114/169] docs: add docs for `unchecked_duration_subtraction` lint --- src/docs/unchecked_duration_subtraction.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/docs/unchecked_duration_subtraction.txt diff --git a/src/docs/unchecked_duration_subtraction.txt b/src/docs/unchecked_duration_subtraction.txt new file mode 100644 index 0000000000000..6b9c9308c87ea --- /dev/null +++ b/src/docs/unchecked_duration_subtraction.txt @@ -0,0 +1,19 @@ +### What it does +Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`]. + +### Why is this bad? +Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or +unintentional panics. + +### Example +``` +let time_passed = Instant::now() - Duration::from_secs(5); +``` + +Use instead: +``` +let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); +``` + +[`Duration`]: std::time::Duration +[`Instant::now()`]: std::time::Instant::now; \ No newline at end of file From b485832b16a388dbcfd3c42030356959e7662a3d Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Sat, 1 Oct 2022 16:50:56 +0200 Subject: [PATCH 115/169] style: remove `dbg!` call --- clippy_lints/src/unchecked_duration_subtraction.rs | 4 +--- tests/ui/unchecked_duration_subtraction.stderr | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/clippy_lints/src/unchecked_duration_subtraction.rs b/clippy_lints/src/unchecked_duration_subtraction.rs index fb08067de8d4f..ae79600066296 100644 --- a/clippy_lints/src/unchecked_duration_subtraction.rs +++ b/clippy_lints/src/unchecked_duration_subtraction.rs @@ -71,9 +71,7 @@ fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr); match expr_ty.kind() { - rustc_middle::ty::Adt(def, _) => { - clippy_utils::match_def_path(cx, dbg!(def).did(), &clippy_utils::paths::INSTANT) - }, + rustc_middle::ty::Adt(def, _) => clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT), _ => false, } } diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr index 827b18a5a0972..6b297a01c7be2 100644 --- a/tests/ui/unchecked_duration_subtraction.stderr +++ b/tests/ui/unchecked_duration_subtraction.stderr @@ -1,4 +1,3 @@ -[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:9:13 | @@ -7,21 +6,18 @@ LL | let _ = _first - second; | = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` -[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:11:13 | LL | let _ = Instant::now() - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap();` -[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:13:13 | LL | let _ = _first - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap();` -[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:15:13 | From 2f2eb2e4baace68868af2f4ac3ec7aed073139ad Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 7 Nov 2022 20:58:49 +0100 Subject: [PATCH 116/169] refactor: lint man. instant elapsed and unch. dur. subtr. in single pass --- ..._subtraction.rs => instant_subtraction.rs} | 78 ++++++++++++++++--- clippy_lints/src/lib.rs | 5 +- clippy_lints/src/manual_instant_elapsed.rs | 69 ---------------- 3 files changed, 71 insertions(+), 81 deletions(-) rename clippy_lints/src/{unchecked_duration_subtraction.rs => instant_subtraction.rs} (52%) delete mode 100644 clippy_lints/src/manual_instant_elapsed.rs diff --git a/clippy_lints/src/unchecked_duration_subtraction.rs b/clippy_lints/src/instant_subtraction.rs similarity index 52% rename from clippy_lints/src/unchecked_duration_subtraction.rs rename to clippy_lints/src/instant_subtraction.rs index ae79600066296..8cc5643c9a7b8 100644 --- a/clippy_lints/src/unchecked_duration_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -1,17 +1,48 @@ -use clippy_utils::{diagnostics, meets_msrv, msrvs, source, ty}; +use clippy_utils::{ + diagnostics::{self, span_lint_and_sugg}, + meets_msrv, msrvs, source, ty, +}; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::sym; +use rustc_span::{source_map::Spanned, sym}; + +declare_clippy_lint! { + /// ### What it does + /// Lints subtraction between `Instant::now()` and another `Instant`. + /// + /// ### Why is this bad? + /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns + /// as `Instant` subtraction saturates. + /// + /// `prev_instant.elapsed()` also more clearly signals intention. + /// + /// ### Example + /// ```rust + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = Instant::now() - prev_instant; + /// ``` + /// Use instead: + /// ```rust + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = prev_instant.elapsed(); + /// ``` + #[clippy::version = "1.64.0"] + pub MANUAL_INSTANT_ELAPSED, + pedantic, + "subtraction between `Instant::now()` and previous `Instant`" +} declare_clippy_lint! { /// ### What it does /// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`]. /// /// ### Why is this bad? - /// Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or + /// Unchecked subtraction could cause underflow on certain platforms, leading to /// unintentional panics. /// /// ### Example @@ -32,21 +63,39 @@ declare_clippy_lint! { "finds unchecked subtraction of a 'Duration' from an 'Instant'" } -pub struct UncheckedDurationSubtraction { +pub struct InstantSubtraction { msrv: Option, } -impl UncheckedDurationSubtraction { +impl InstantSubtraction { #[must_use] pub fn new(msrv: Option) -> Self { Self { msrv } } } -impl_lint_pass!(UncheckedDurationSubtraction => [UNCHECKED_DURATION_SUBTRACTION]); +impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]); + +impl LateLintPass<'_> for InstantSubtraction { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind + && check_instant_now_call(cx, lhs) + && let ty_resolved = cx.typeck_results().expr_ty(rhs) + && let rustc_middle::ty::Adt(def, _) = ty_resolved.kind() + && clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT) + && let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs) + { + span_lint_and_sugg( + cx, + MANUAL_INSTANT_ELAPSED, + expr.span, + "manual implementation of `Instant::elapsed`", + "try", + format!("{}.elapsed()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); + } -impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) { return; } @@ -67,6 +116,17 @@ impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction { } } +fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { + if let ExprKind::Call(fn_expr, []) = expr_block.kind + && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) + && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) + { + true + } else { + false + } +} + fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a5407b65b88d1..ce0e7ebfbf1b6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -151,6 +151,7 @@ mod inherent_impl; mod inherent_to_string; mod init_numbered_fields; mod inline_fn_without_body; +mod instant_subtraction; mod int_plus_one; mod invalid_upcast_comparisons; mod invalid_utf8_in_unchecked; @@ -172,7 +173,6 @@ mod manual_assert; mod manual_async_fn; mod manual_bits; mod manual_clamp; -mod manual_instant_elapsed; mod manual_is_ascii_check; mod manual_let_else; mod manual_non_exhaustive; @@ -279,7 +279,6 @@ mod trailing_empty_array; mod trait_bounds; mod transmute; mod types; -mod unchecked_duration_subtraction; mod undocumented_unsafe_blocks; mod unicode; mod uninit_vec; @@ -908,7 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed)); + store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); diff --git a/clippy_lints/src/manual_instant_elapsed.rs b/clippy_lints/src/manual_instant_elapsed.rs deleted file mode 100644 index 1e60aa02d3cad..0000000000000 --- a/clippy_lints/src/manual_instant_elapsed.rs +++ /dev/null @@ -1,69 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; - -declare_clippy_lint! { - /// ### What it does - /// Lints subtraction between `Instant::now()` and another `Instant`. - /// - /// ### Why is this bad? - /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns - /// as `Instant` subtraction saturates. - /// - /// `prev_instant.elapsed()` also more clearly signals intention. - /// - /// ### Example - /// ```rust - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = Instant::now() - prev_instant; - /// ``` - /// Use instead: - /// ```rust - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = prev_instant.elapsed(); - /// ``` - #[clippy::version = "1.65.0"] - pub MANUAL_INSTANT_ELAPSED, - pedantic, - "subtraction between `Instant::now()` and previous `Instant`" -} - -declare_lint_pass!(ManualInstantElapsed => [MANUAL_INSTANT_ELAPSED]); - -impl LateLintPass<'_> for ManualInstantElapsed { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind - && check_instant_now_call(cx, lhs) - && let ty_resolved = cx.typeck_results().expr_ty(rhs) - && let rustc_middle::ty::Adt(def, _) = ty_resolved.kind() - && clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT) - && let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs) - { - span_lint_and_sugg( - cx, - MANUAL_INSTANT_ELAPSED, - expr.span, - "manual implementation of `Instant::elapsed`", - "try", - format!("{}.elapsed()", sugg.maybe_par()), - Applicability::MachineApplicable, - ); - } - } -} - -fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { - if let ExprKind::Call(fn_expr, []) = expr_block.kind - && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) - && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) - { - true - } else { - false - } -} From a566eb37659ecbdcfe1403f8ccab753714dbec60 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 7 Nov 2022 20:59:55 +0100 Subject: [PATCH 117/169] docs: update docs for unchecked duration subtr. lint --- src/docs/unchecked_duration_subtraction.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/unchecked_duration_subtraction.txt b/src/docs/unchecked_duration_subtraction.txt index 6b9c9308c87ea..15a4c02c60654 100644 --- a/src/docs/unchecked_duration_subtraction.txt +++ b/src/docs/unchecked_duration_subtraction.txt @@ -2,7 +2,7 @@ Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`]. ### Why is this bad? -Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or +Unchecked subtraction could cause underflow on certain platforms, leading to unintentional panics. ### Example From 80e3553f290cd252646c61086e9461fc7618b694 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 7 Nov 2022 21:34:24 +0100 Subject: [PATCH 118/169] refactor: improve code re-use in InstantSubtraction lint pass --- clippy_lints/src/instant_subtraction.rs | 85 +++++++++++++++---------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 8cc5643c9a7b8..3166c62dad43e 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -1,6 +1,8 @@ use clippy_utils::{ diagnostics::{self, span_lint_and_sugg}, - meets_msrv, msrvs, source, ty, + meets_msrv, msrvs, source, + sugg::Sugg, + ty, }; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -78,45 +80,41 @@ impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATIO impl LateLintPass<'_> for InstantSubtraction { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind - && check_instant_now_call(cx, lhs) - && let ty_resolved = cx.typeck_results().expr_ty(rhs) - && let rustc_middle::ty::Adt(def, _) = ty_resolved.kind() - && clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT) - && let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs) + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + lhs, + rhs, + ) = expr.kind { - span_lint_and_sugg( - cx, - MANUAL_INSTANT_ELAPSED, - expr.span, - "manual implementation of `Instant::elapsed`", - "try", - format!("{}.elapsed()", sugg.maybe_par()), - Applicability::MachineApplicable, - ); - } - - if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) { - return; - } - - if_chain! { - if let ExprKind::Binary(op, lhs, rhs) = expr.kind; - - if let BinOpKind::Sub = op.node; - - // get types of left and right side - if is_an_instant(cx, lhs); - if is_a_duration(cx, rhs); - - then { - print_lint_and_sugg(cx, lhs, rhs, expr) + if_chain! { + if is_instant_now_call(cx, lhs); + + if is_an_instant(cx, rhs); + if let Some(sugg) = Sugg::hir_opt(cx, rhs); + + then { + print_manual_instant_elapsed_sugg(cx, expr, sugg) + } else { + if_chain! { + if !expr.span.from_expansion(); + if meets_msrv(self.msrv, msrvs::TRY_FROM); + + if is_an_instant(cx, lhs); + if is_a_duration(cx, rhs); + + then { + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr) + } + } + } } } } } -fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { +fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, []) = expr_block.kind && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) @@ -141,7 +139,24 @@ fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration) } -fn print_lint_and_sugg(cx: &LateContext<'_>, left_expr: &Expr<'_>, right_expr: &Expr<'_>, expr: &Expr<'_>) { +fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { + span_lint_and_sugg( + cx, + MANUAL_INSTANT_ELAPSED, + expr.span, + "manual implementation of `Instant::elapsed`", + "try", + format!("{}.elapsed()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); +} + +fn print_unchecked_duration_subtraction_sugg( + cx: &LateContext<'_>, + left_expr: &Expr<'_>, + right_expr: &Expr<'_>, + expr: &Expr<'_>, +) { let mut applicability = Applicability::MachineApplicable; let left_expr = From 4bd6d0beb0ef8ad72ad8ce6a28eb25a48f9cbc4b Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 7 Nov 2022 21:37:04 +0100 Subject: [PATCH 119/169] test: update tests for manual_instant_elapsed lint --- tests/ui/manual_instant_elapsed.fixed | 1 + tests/ui/manual_instant_elapsed.rs | 1 + tests/ui/manual_instant_elapsed.stderr | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/ui/manual_instant_elapsed.fixed b/tests/ui/manual_instant_elapsed.fixed index 0fa776b7b2e4e..85a91543c893a 100644 --- a/tests/ui/manual_instant_elapsed.fixed +++ b/tests/ui/manual_instant_elapsed.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::unchecked_duration_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_instant_elapsed.rs b/tests/ui/manual_instant_elapsed.rs index 5b11b84535ddc..c98cb15b91649 100644 --- a/tests/ui/manual_instant_elapsed.rs +++ b/tests/ui/manual_instant_elapsed.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::unchecked_duration_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_instant_elapsed.stderr b/tests/ui/manual_instant_elapsed.stderr index 5537f5642a23c..4ce1f689107e9 100644 --- a/tests/ui/manual_instant_elapsed.stderr +++ b/tests/ui/manual_instant_elapsed.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Instant::elapsed` - --> $DIR/manual_instant_elapsed.rs:17:20 + --> $DIR/manual_instant_elapsed.rs:18:20 | LL | let duration = Instant::now() - prev_instant; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `prev_instant.elapsed()` @@ -7,7 +7,7 @@ LL | let duration = Instant::now() - prev_instant; = note: `-D clippy::manual-instant-elapsed` implied by `-D warnings` error: manual implementation of `Instant::elapsed` - --> $DIR/manual_instant_elapsed.rs:26:5 + --> $DIR/manual_instant_elapsed.rs:27:5 | LL | Instant::now() - *ref_to_instant; // to ensure parens are added correctly | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*ref_to_instant).elapsed()` From 852d80245148cdd23c5516bea015b0f3bf277ed4 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 7 Nov 2022 22:21:32 +0100 Subject: [PATCH 120/169] style: inline variables in `format!` --- clippy_lints/src/instant_subtraction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 3166c62dad43e..c039f1b5c6a60 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -174,7 +174,7 @@ fn print_unchecked_duration_subtraction_sugg( expr.span, "unchecked subtraction of a 'Duration' from an 'Instant'", "try", - format!("{}.checked_sub({}).unwrap();", left_expr, right_expr), + format!("{left_expr}.checked_sub({right_expr}).unwrap();"), applicability, ); } From 62ab4fb9c265b9e25d61c99511cd26de0260f435 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Tue, 8 Nov 2022 10:22:58 +0100 Subject: [PATCH 121/169] fix: add rust-fix annotation to unckd. duration subtr. lint test --- tests/ui/unchecked_duration_subtraction.fixed | 17 +++++++++++++++++ tests/ui/unchecked_duration_subtraction.rs | 1 + tests/ui/unchecked_duration_subtraction.stderr | 8 ++++---- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 tests/ui/unchecked_duration_subtraction.fixed diff --git a/tests/ui/unchecked_duration_subtraction.fixed b/tests/ui/unchecked_duration_subtraction.fixed new file mode 100644 index 0000000000000..bcc231d93b162 --- /dev/null +++ b/tests/ui/unchecked_duration_subtraction.fixed @@ -0,0 +1,17 @@ +// run-rustfix +#![warn(clippy::unchecked_duration_subtraction)] + +use std::time::{Duration, Instant}; + +fn main() { + let _first = Instant::now(); + let second = Duration::from_secs(3); + + let _ = _first.checked_sub(second).unwrap();; + + let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap();; + + let _ = _first.checked_sub(Duration::from_secs(5)).unwrap();; + + let _ = Instant::now().checked_sub(second).unwrap();; +} diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_duration_subtraction.rs index fff1d13720d98..a14a7ea57cc50 100644 --- a/tests/ui/unchecked_duration_subtraction.rs +++ b/tests/ui/unchecked_duration_subtraction.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::unchecked_duration_subtraction)] use std::time::{Duration, Instant}; diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr index 6b297a01c7be2..b949dbf701a02 100644 --- a/tests/ui/unchecked_duration_subtraction.stderr +++ b/tests/ui/unchecked_duration_subtraction.stderr @@ -1,5 +1,5 @@ error: unchecked subtraction of a 'Duration' from an 'Instant' - --> $DIR/unchecked_duration_subtraction.rs:9:13 + --> $DIR/unchecked_duration_subtraction.rs:10:13 | LL | let _ = _first - second; | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap();` @@ -7,19 +7,19 @@ LL | let _ = _first - second; = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` error: unchecked subtraction of a 'Duration' from an 'Instant' - --> $DIR/unchecked_duration_subtraction.rs:11:13 + --> $DIR/unchecked_duration_subtraction.rs:12:13 | LL | let _ = Instant::now() - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap();` error: unchecked subtraction of a 'Duration' from an 'Instant' - --> $DIR/unchecked_duration_subtraction.rs:13:13 + --> $DIR/unchecked_duration_subtraction.rs:14:13 | LL | let _ = _first - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap();` error: unchecked subtraction of a 'Duration' from an 'Instant' - --> $DIR/unchecked_duration_subtraction.rs:15:13 + --> $DIR/unchecked_duration_subtraction.rs:16:13 | LL | let _ = Instant::now() - second; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap();` From 862ac29192bd4e2c9db04e9763dfd1a35a00a9cb Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Tue, 8 Nov 2022 15:04:32 +0100 Subject: [PATCH 122/169] fix: remove (redundant) semicolon in lint suggestion --- clippy_lints/src/instant_subtraction.rs | 2 +- tests/ui/unchecked_duration_subtraction.fixed | 8 ++++---- tests/ui/unchecked_duration_subtraction.stderr | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index c039f1b5c6a60..d83beb622cfdc 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -174,7 +174,7 @@ fn print_unchecked_duration_subtraction_sugg( expr.span, "unchecked subtraction of a 'Duration' from an 'Instant'", "try", - format!("{left_expr}.checked_sub({right_expr}).unwrap();"), + format!("{left_expr}.checked_sub({right_expr}).unwrap()"), applicability, ); } diff --git a/tests/ui/unchecked_duration_subtraction.fixed b/tests/ui/unchecked_duration_subtraction.fixed index bcc231d93b162..a0e49a8beb1ed 100644 --- a/tests/ui/unchecked_duration_subtraction.fixed +++ b/tests/ui/unchecked_duration_subtraction.fixed @@ -7,11 +7,11 @@ fn main() { let _first = Instant::now(); let second = Duration::from_secs(3); - let _ = _first.checked_sub(second).unwrap();; + let _ = _first.checked_sub(second).unwrap(); - let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap();; + let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap(); - let _ = _first.checked_sub(Duration::from_secs(5)).unwrap();; + let _ = _first.checked_sub(Duration::from_secs(5)).unwrap(); - let _ = Instant::now().checked_sub(second).unwrap();; + let _ = Instant::now().checked_sub(second).unwrap(); } diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr index b949dbf701a02..a2e0aa1d7c089 100644 --- a/tests/ui/unchecked_duration_subtraction.stderr +++ b/tests/ui/unchecked_duration_subtraction.stderr @@ -2,7 +2,7 @@ error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:10:13 | LL | let _ = _first - second; - | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap();` + | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()` | = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` @@ -10,19 +10,19 @@ error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:12:13 | LL | let _ = Instant::now() - Duration::from_secs(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()` error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:14:13 | LL | let _ = _first - Duration::from_secs(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()` error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:16:13 | LL | let _ = Instant::now() - second; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap();` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` error: aborting due to 4 previous errors From 36eac0cb4ae2e372193f73c3181d5290431d3ae4 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Thu, 10 Nov 2022 15:55:45 +0100 Subject: [PATCH 123/169] fix: update lints --- clippy_lints/src/declared_lints.rs | 3 ++- clippy_lints/src/lib.rs | 1 - src/docs/unchecked_duration_subtraction.txt | 19 ------------------- 3 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 src/docs/unchecked_duration_subtraction.txt diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 98265410e8490..7a705e563fc92 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -202,6 +202,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO, crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, + crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO, + crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, crate::invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED_INFO, @@ -250,7 +252,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, crate::manual_bits::MANUAL_BITS_INFO, crate::manual_clamp::MANUAL_CLAMP_INFO, - crate::manual_instant_elapsed::MANUAL_INSTANT_ELAPSED_INFO, crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ce0e7ebfbf1b6..3cfb2f491bc3e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -921,7 +921,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv))); - store.register_late_pass(move || Box::new(unchecked_duration_subtraction::UncheckedDurationSubtraction::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/docs/unchecked_duration_subtraction.txt b/src/docs/unchecked_duration_subtraction.txt deleted file mode 100644 index 15a4c02c60654..0000000000000 --- a/src/docs/unchecked_duration_subtraction.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`]. - -### Why is this bad? -Unchecked subtraction could cause underflow on certain platforms, leading to -unintentional panics. - -### Example -``` -let time_passed = Instant::now() - Duration::from_secs(5); -``` - -Use instead: -``` -let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); -``` - -[`Duration`]: std::time::Duration -[`Instant::now()`]: std::time::Instant::now; \ No newline at end of file From 2bc04bdac26fbb49e3b4ef3673f6a5143df307e7 Mon Sep 17 00:00:00 2001 From: koka Date: Fri, 11 Nov 2022 00:14:18 +0900 Subject: [PATCH 124/169] fix: cognitive_complexity for async fn --- clippy_lints/src/cognitive_complexity.rs | 21 +++++++++++++++------ tests/ui/cognitive_complexity.rs | 8 ++++++++ tests/ui/cognitive_complexity.stderr | 10 +++++++++- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 77af3b53d6333..1c3a89a97824c 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -4,11 +4,11 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr; -use clippy_utils::LimitStack; +use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack}; use core::ops::ControlFlow; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, HirId}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -56,15 +56,13 @@ impl CognitiveComplexity { cx: &LateContext<'tcx>, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, - body: &'tcx Body<'_>, + expr: &'tcx Expr<'_>, body_span: Span, ) { if body_span.from_expansion() { return; } - let expr = body.value; - let mut cc = 1u64; let mut returns = 0u64; let _: Option = for_each_expr(expr, |e| { @@ -146,7 +144,18 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { ) { let def_id = cx.tcx.hir().local_def_id(hir_id); if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) { - self.check(cx, kind, decl, body, span); + let expr = if is_async_fn(kind) { + match get_async_fn_body(cx.tcx, body) { + Some(b) => b, + None => { + return; + }, + } + } else { + body.value + }; + + self.check(cx, kind, decl, expr, span); } } diff --git a/tests/ui/cognitive_complexity.rs b/tests/ui/cognitive_complexity.rs index 912e6788afddc..498a9f0644f50 100644 --- a/tests/ui/cognitive_complexity.rs +++ b/tests/ui/cognitive_complexity.rs @@ -393,3 +393,11 @@ impl Moo { } } } + +#[clippy::cognitive_complexity = "1"] +mod issue9300 { + async fn a() { + let a = 0; + if a == 0 {} + } +} diff --git a/tests/ui/cognitive_complexity.stderr b/tests/ui/cognitive_complexity.stderr index d7f2f24e52f2b..208f4d7b3474a 100644 --- a/tests/ui/cognitive_complexity.stderr +++ b/tests/ui/cognitive_complexity.stderr @@ -135,5 +135,13 @@ LL | fn moo(&self) { | = help: you could split it up into multiple smaller functions -error: aborting due to 17 previous errors +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:399:14 + | +LL | async fn a() { + | ^ + | + = help: you could split it up into multiple smaller functions + +error: aborting due to 18 previous errors From 3790aa3d5d14ef79b27776767985716844861f33 Mon Sep 17 00:00:00 2001 From: hrxi Date: Thu, 10 Nov 2022 19:42:20 +0100 Subject: [PATCH 125/169] Make `bool_to_int_with_if` a pedantic lint In all the cases I've observed, it did not make the code clearer. Using bools as integer is frowned upon in some languages, in others it's simply not possible. You can find comments on the original pull request #8131 that agree with this point of view. --- clippy_lints/src/bool_to_int_with_if.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index 40ded4056696e..bdb3a01160272 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// this lint suggests using a `from()` function or an `as` coercion. /// /// ### Why is this bad? - /// Coercion or `from()` is idiomatic way to convert bool to a number. + /// Coercion or `from()` is another way to convert bool to a number. /// Both methods are guaranteed to return 1 for true, and 0 for false. /// /// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E @@ -39,7 +39,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.65.0"] pub BOOL_TO_INT_WITH_IF, - style, + pedantic, "using if to convert bool to int" } declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); From 7ddd321ecd41d8b074d06d21393fed43a85fa150 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Mon, 31 Oct 2022 18:30:09 +0000 Subject: [PATCH 126/169] Introduce `ExprKind::IncludedBytes` --- clippy_utils/src/sugg.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index aad7da61a8a54..eefba8cd29c41 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -207,6 +207,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::InlineAsm(..) | ast::ExprKind::ConstBlock(..) | ast::ExprKind::Lit(..) + | ast::ExprKind::IncludedBytes(..) | ast::ExprKind::Loop(..) | ast::ExprKind::MacCall(..) | ast::ExprKind::MethodCall(..) From 39398e163ac7508b585f07821e5be195102a4017 Mon Sep 17 00:00:00 2001 From: koka Date: Sat, 12 Nov 2022 19:15:21 +0900 Subject: [PATCH 127/169] fix: use HasPlaceholders * remove unnecessary mutability * fix typo --- clippy_lints/src/manual_is_ascii_check.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 3a6b693f7662c..bb8c142f8e468 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -91,15 +91,16 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { CharRange::Digit => Some("is_ascii_digit"), CharRange::Otherwise => None, } { - let mut applicability = Applicability::MaybeIncorrect; let default_snip = ".."; // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for - // macro span, so we check applicability manually by comaring `recv` is not default. + // macro span, so we check applicability manually by comparing `recv` is not default. let recv = snippet(cx, recv.span, default_snip); - if recv != default_snip { - applicability = Applicability::MachineApplicable; - } + let applicability = if recv == default_snip { + Applicability::HasPlaceholders + } else { + Applicability::MachineApplicable + }; span_lint_and_sugg( cx, From 93edc127a0d9a5ecd403a2f72ff6c02c5fadb696 Mon Sep 17 00:00:00 2001 From: koka Date: Sat, 12 Nov 2022 20:31:25 +0900 Subject: [PATCH 128/169] Avoid lint to unsized mutable reference --- clippy_lints/src/mut_mut.rs | 16 +++++++++------- tests/ui/mut_mut.rs | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index cb16f00047a39..ae17327fd2a63 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -68,13 +68,15 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { expr.span, "generally you want to avoid `&mut &mut _` if possible", ); - } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { - span_lint( - self.cx, - MUT_MUT, - expr.span, - "this expression mutably borrows a mutable reference. Consider reborrowing", - ); + } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { + if ty.peel_refs().is_sized(self.cx.tcx.at(expr.span), self.cx.param_env) { + span_lint( + self.cx, + MUT_MUT, + expr.span, + "this expression mutably borrows a mutable reference. Consider reborrowing", + ); + } } } } diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index ac8fd9d8fb093..ee3a856566cc1 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -57,3 +57,20 @@ fn issue6922() { // do not lint from an external macro mut_mut!(); } + +mod issue9035 { + use std::fmt::Display; + + struct Foo<'a> { + inner: &'a mut dyn Display, + } + + impl Foo<'_> { + fn foo(&mut self) { + let hlp = &mut self.inner; + bar(hlp); + } + } + + fn bar(_: &mut impl Display) {} +} From 34c4520eae7adf1a3adf3fce5376eac2911d08b8 Mon Sep 17 00:00:00 2001 From: koka Date: Sat, 12 Nov 2022 22:36:20 +0900 Subject: [PATCH 129/169] Fix is_async_fn to check FnKind::Method --- clippy_utils/src/lib.rs | 6 +++++- tests/ui/cognitive_complexity.rs | 8 ++++++++ tests/ui/cognitive_complexity.stderr | 10 +++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index f7d3c91777ea6..ac4d0b89f17e4 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1901,7 +1901,11 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, /// Checks if the given function kind is an async function. pub fn is_async_fn(kind: FnKind<'_>) -> bool { - matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async) + match kind { + FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async, + FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async, + FnKind::Closure => false, + } } /// Peels away all the compiler generated code surrounding the body of an async function, diff --git a/tests/ui/cognitive_complexity.rs b/tests/ui/cognitive_complexity.rs index 498a9f0644f50..07bdaff00dc41 100644 --- a/tests/ui/cognitive_complexity.rs +++ b/tests/ui/cognitive_complexity.rs @@ -400,4 +400,12 @@ mod issue9300 { let a = 0; if a == 0 {} } + + pub struct S; + impl S { + pub async fn async_method() { + let a = 0; + if a == 0 {} + } + } } diff --git a/tests/ui/cognitive_complexity.stderr b/tests/ui/cognitive_complexity.stderr index 208f4d7b3474a..5824631fa83ba 100644 --- a/tests/ui/cognitive_complexity.stderr +++ b/tests/ui/cognitive_complexity.stderr @@ -143,5 +143,13 @@ LL | async fn a() { | = help: you could split it up into multiple smaller functions -error: aborting due to 18 previous errors +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:406:22 + | +LL | pub async fn async_method() { + | ^^^^^^^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: aborting due to 19 previous errors From 989986144c889051eb3014cc1fdc0891bbb483f5 Mon Sep 17 00:00:00 2001 From: Kartavya Vashishtha Date: Sat, 12 Nov 2022 20:36:30 +0530 Subject: [PATCH 130/169] fix never_loop false positive on unconditional break to internal labeled block ref #9831 --- clippy_lints/src/loops/never_loop.rs | 3 ++- tests/ui/never_loop.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 16b00ad663787..abb18187ef147 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -169,7 +169,8 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { combine_seq(e, arms) } }, - ExprKind::Block(b, _) => never_loop_block(b, main_loop_id), + ExprKind::Block(b, None) => never_loop_block(b, main_loop_id), + ExprKind::Block(b, Some(_label)) => absorb_break(never_loop_block(b, main_loop_id)), ExprKind::Continue(d) => { let id = d .target_id diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 3dbef19890e9c..86a5d03f765f6 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -229,6 +229,18 @@ pub fn test18() { }; } +// Issue #9831: unconditional break to internal labeled block +pub fn test19() { + fn thing(iter: impl Iterator) { + for _ in iter { + 'b: { + // error goes away if we just have the block's value be (). + break 'b; + } + } + } +} + fn main() { test1(); test2(); From 243661b739b122f51a17e64161d9f3a1324b8d6a Mon Sep 17 00:00:00 2001 From: hrxi Date: Sat, 12 Nov 2022 22:26:09 +0100 Subject: [PATCH 131/169] Make it clear that `or_fun_call` can be a false-positive Also move it to nursery so that the false-positives can be dealt with. CC #8574 --- clippy_lints/src/methods/mod.rs | 22 ++++++++++------------ tests/ui/unwrap_or.rs | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 82a2cc62d8205..53f9c355c5d73 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -832,32 +832,30 @@ declare_clippy_lint! { /// etc. instead. /// /// ### Why is this bad? - /// The function will always be called and potentially - /// allocate an object acting as the default. + /// The function will always be called. This is only bad if it allocates or + /// does some non-trivial amount of work. /// /// ### Known problems - /// If the function has side-effects, not calling it will - /// change the semantic of the program, but you shouldn't rely on that anyway. + /// If the function has side-effects, not calling it will change the + /// semantic of the program, but you shouldn't rely on that. + /// + /// The lint also cannot figure out whether the function you call is + /// actually expensive to call or not. /// /// ### Example /// ```rust /// # let foo = Some(String::new()); - /// foo.unwrap_or(String::new()); + /// foo.unwrap_or(String::from("empty")); /// ``` /// /// Use instead: /// ```rust /// # let foo = Some(String::new()); - /// foo.unwrap_or_else(String::new); - /// - /// // or - /// - /// # let foo = Some(String::new()); - /// foo.unwrap_or_default(); + /// foo.unwrap_or_else(|| String::from("empty")); /// ``` #[clippy::version = "pre 1.29.0"] pub OR_FUN_CALL, - perf, + nursery, "using any `*or` method with a function call, which suggests `*or_else`" } diff --git a/tests/ui/unwrap_or.rs b/tests/ui/unwrap_or.rs index bfb41e4394731..a0c003f5b1eac 100644 --- a/tests/ui/unwrap_or.rs +++ b/tests/ui/unwrap_or.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all)] +#![warn(clippy::all, clippy::or_fun_call)] fn main() { let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); From e6ef47887770fd3414f57f0725b6826bec80031c Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 6 Nov 2022 19:46:55 +0000 Subject: [PATCH 132/169] Store a LocalDefId in hir::Variant & hir::Field. --- clippy_lints/src/manual_non_exhaustive.rs | 6 +++--- clippy_lints/src/missing_doc.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 6806c1466968d..4877cee0cc1ea 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -157,10 +157,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { && def.variants.len() > 1 { let mut iter = def.variants.iter().filter_map(|v| { - let id = cx.tcx.hir().local_def_id(v.id); - (matches!(v.data, hir::VariantData::Unit(_)) + let id = cx.tcx.hir().local_def_id(v.hir_id); + (matches!(v.data, hir::VariantData::Unit(..)) && v.ident.as_str().starts_with('_') - && is_doc_hidden(cx.tcx.hir().attrs(v.id))) + && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))) .then_some((id, v.span)) }); if let Some((id, span)) = iter.next() diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 2a63681db60e5..6fd100762b49d 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { - let attrs = cx.tcx.hir().attrs(v.id); + let attrs = cx.tcx.hir().attrs(v.hir_id); if !is_from_proc_macro(cx, v) { self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant"); } From 8a2d0f255d623390c9495dec1c9e5d22bc895e5d Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 13 Nov 2022 22:58:20 +0000 Subject: [PATCH 133/169] Fix clippy and rustdoc please, please, don't match on `Symbol::as_str`s, every time you do, somewhere in the world another waffle becomes sad... --- clippy_utils/src/macros.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 9a682fbe604ff..d13b34a66cca7 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -199,12 +199,12 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option< pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool { let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false }; matches!( - name.as_str(), - "core_panic_macro" - | "std_panic_macro" - | "core_panic_2015_macro" - | "std_panic_2015_macro" - | "core_panic_2021_macro" + name, + sym::core_panic_macro + | sym::std_panic_macro + | sym::core_panic_2015_macro + | sym::std_panic_2015_macro + | sym::core_panic_2021_macro ) } From b8357ffd1f2b3f7cd12b7cd29d32c08a5ec5b7c5 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Mon, 14 Nov 2022 16:06:21 +0100 Subject: [PATCH 134/169] fix `vec-box-size-threshold` off-by-one error --- clippy_lints/src/types/vec_box.rs | 2 +- tests/ui-toml/vec_box_sized/test.rs | 5 +++-- tests/ui-toml/vec_box_sized/test.stderr | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 6c329d8cdf196..02703ebecb928 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -42,7 +42,7 @@ pub(super) fn check( if !ty_ty.has_escaping_bound_vars(); if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env); if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); - if ty_ty_size <= box_size_threshold; + if ty_ty_size < box_size_threshold; then { span_lint_and_sugg( cx, diff --git a/tests/ui-toml/vec_box_sized/test.rs b/tests/ui-toml/vec_box_sized/test.rs index bf04bee16373c..4c46deb585b6c 100644 --- a/tests/ui-toml/vec_box_sized/test.rs +++ b/tests/ui-toml/vec_box_sized/test.rs @@ -7,8 +7,9 @@ struct C { } struct Foo(Vec>); -struct Bar(Vec>); -struct Baz(Vec>); +struct Bar(Vec>); +struct Quux(Vec>); +struct Baz(Vec>); struct BarBaz(Vec>); struct FooBarBaz(Vec>); diff --git a/tests/ui-toml/vec_box_sized/test.stderr b/tests/ui-toml/vec_box_sized/test.stderr index cf194de3c5537..55de68f8ecf47 100644 --- a/tests/ui-toml/vec_box_sized/test.stderr +++ b/tests/ui-toml/vec_box_sized/test.stderr @@ -9,11 +9,11 @@ LL | struct Foo(Vec>); error: `Vec` is already on the heap, the boxing is unnecessary --> $DIR/test.rs:10:12 | -LL | struct Bar(Vec>); - | ^^^^^^^^^^^^^ help: try: `Vec` +LL | struct Bar(Vec>); + | ^^^^^^^^^^^^^ help: try: `Vec` error: `Vec` is already on the heap, the boxing is unnecessary - --> $DIR/test.rs:13:18 + --> $DIR/test.rs:14:18 | LL | struct FooBarBaz(Vec>); | ^^^^^^^^^^^ help: try: `Vec` From 3f89ab0618a1075f0c53c47d405c0f6e9bf43953 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 14 Nov 2022 22:00:09 +0100 Subject: [PATCH 135/169] chore: update lint version of MANUAL_INSTAN_ELAPSED to 1.65 --- clippy_lints/src/instant_subtraction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index d83beb622cfdc..6d23a672856fa 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// let prev_instant = Instant::now(); /// let duration = prev_instant.elapsed(); /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub MANUAL_INSTANT_ELAPSED, pedantic, "subtraction between `Instant::now()` and previous `Instant`" From 912dc919af03427288b64921bb4aea0010d071bf Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 14 Nov 2022 22:08:11 +0100 Subject: [PATCH 136/169] docs: update unchecked duration subtraction lint doc --- clippy_lints/src/instant_subtraction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 6d23a672856fa..3f72222f31b6a 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -41,7 +41,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`]. + /// Lints subtraction between an [`Instant`] and a [`Duration`]. /// /// ### Why is this bad? /// Unchecked subtraction could cause underflow on certain platforms, leading to From 72ab91d8066ee399cff3e3c8293bf4dbccfb6358 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 14 Nov 2022 22:25:56 +0100 Subject: [PATCH 137/169] fix: add extract_msrv_attr call to instan_subtraction lint pass --- clippy_lints/src/instant_subtraction.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 3f72222f31b6a..716c11becd9cb 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -112,6 +112,8 @@ impl LateLintPass<'_> for InstantSubtraction { } } } + + extract_msrv_attr!(LateContext); } fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { From 0dd6ce0b1931e3e5c274daf4bbf50e96a322befd Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 14 Nov 2022 22:37:25 +0100 Subject: [PATCH 138/169] docs: import Instant and Duration in doctests --- clippy_lints/src/instant_subtraction.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 716c11becd9cb..60754b224fc84 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -49,11 +49,13 @@ declare_clippy_lint! { /// /// ### Example /// ```rust + /// # use std::time::{Instant, Duration}; /// let time_passed = Instant::now() - Duration::from_secs(5); /// ``` /// /// Use instead: /// ```rust + /// # use std::time::{Instant, Duration}; /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); /// ``` /// From 7c4611c7e170a7eb6a642f9267b95a14f0cd54de Mon Sep 17 00:00:00 2001 From: koka Date: Tue, 15 Nov 2022 12:58:17 +0900 Subject: [PATCH 139/169] Allow return types for closures with lifetime binder --- clippy_lints/src/unused_unit.rs | 8 +++++-- tests/ui/unused_unit.fixed | 7 ++++++ tests/ui/unused_unit.rs | 7 ++++++ tests/ui/unused_unit.stderr | 40 ++++++++++++++++----------------- 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index cd1d90e860b9f..cad8da18c2fbc 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{position_before_rarrow, snippet_opt}; use if_chain::if_chain; -use rustc_ast::ast; -use rustc_ast::visit::FnKind; +use rustc_ast::{ast, visit::FnKind, ClosureBinder}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -43,6 +42,11 @@ impl EarlyLintPass for UnusedUnit { 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 { + // implicit types in closure signatures are forbidden when `for<...>` is present + if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { + return; + } + lint_unneeded_unit_return(cx, ty, span); } } diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 7bb43cf7ae82d..3dd640b86f0b3 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -7,6 +7,7 @@ // test of the JSON error format. #![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] #![rustfmt::skip] #![deny(clippy::unused_unit)] @@ -87,3 +88,9 @@ fn macro_expr() { } e!() } + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 21073fb802ada..bddecf06fb76d 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -7,6 +7,7 @@ // test of the JSON error format. #![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] #![rustfmt::skip] #![deny(clippy::unused_unit)] @@ -87,3 +88,9 @@ fn macro_expr() { } e!() } + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index 0d2cb77855be1..ce06738cfe473 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,119 +1,119 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:58 + --> $DIR/unused_unit.rs:20:58 | 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 + --> $DIR/unused_unit.rs:13:9 | LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:28 + --> $DIR/unused_unit.rs:20:28 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:20:18 + --> $DIR/unused_unit.rs:21:18 | LL | where G: Fn() -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:21:26 + --> $DIR/unused_unit.rs:22:26 | LL | let _y: &dyn Fn() -> () = &f; | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:28:18 + --> $DIR/unused_unit.rs:29:18 | LL | fn into(self) -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:29:9 + --> $DIR/unused_unit.rs:30:9 | LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:34:29 + --> $DIR/unused_unit.rs:35: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:36:19 + --> $DIR/unused_unit.rs:37:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:37:16 + --> $DIR/unused_unit.rs:38:16 | LL | H: Fn() -> (); | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:41:29 + --> $DIR/unused_unit.rs:42: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:43:19 + --> $DIR/unused_unit.rs:44:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:44:16 + --> $DIR/unused_unit.rs:45:16 | LL | H: Fn() -> () {} | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:47:17 + --> $DIR/unused_unit.rs:48:17 | LL | fn return_unit() -> () { () } | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:47:26 + --> $DIR/unused_unit.rs:48:26 | LL | fn return_unit() -> () { () } | ^^ help: remove the final `()` error: unneeded `()` - --> $DIR/unused_unit.rs:57:14 + --> $DIR/unused_unit.rs:58:14 | LL | break(); | ^^ help: remove the `()` error: unneeded `()` - --> $DIR/unused_unit.rs:59:11 + --> $DIR/unused_unit.rs:60:11 | LL | return(); | ^^ help: remove the `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:76:10 + --> $DIR/unused_unit.rs:77:10 | LL | fn test()->(){} | ^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:79:11 + --> $DIR/unused_unit.rs:80:11 | LL | fn test2() ->(){} | ^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:82:11 + --> $DIR/unused_unit.rs:83:11 | LL | fn test3()-> (){} | ^^^^^ help: remove the `-> ()` From 93ac0f58bfeb8046a6e49b79fbaca0cb27d92f56 Mon Sep 17 00:00:00 2001 From: Aphek Date: Tue, 15 Nov 2022 01:57:56 -0300 Subject: [PATCH 140/169] Keep `ref` on `infallible_destructuring_match` suggestion --- .../matches/infallible_destructuring_match.rs | 7 ++++--- tests/ui/infallible_destructuring_match.fixed | 12 ++++++++++++ tests/ui/infallible_destructuring_match.rs | 14 ++++++++++++++ tests/ui/infallible_destructuring_match.stderr | 16 ++++++++++++---- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/matches/infallible_destructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs index 2472acb6f6e8b..d18c92caba2a6 100644 --- a/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; -use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath}; +use rustc_hir::{ByRef, ExprKind, Local, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; use super::INFALLIBLE_DESTRUCTURING_MATCH; @@ -16,7 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { if let PatKind::TupleStruct( 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; + if let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind; let body = peel_blocks(arms[0].body); if path_to_local_id(body, arg); @@ -30,8 +30,9 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { Consider using `let`", "try this", format!( - "let {}({}) = {};", + "let {}({}{}) = {};", snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), + if binding.0 == ByRef::Yes { "ref " } else { "" }, snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), snippet_with_applicability(cx, target.span, "..", &mut applicability), ), diff --git a/tests/ui/infallible_destructuring_match.fixed b/tests/ui/infallible_destructuring_match.fixed index b8e40d995531a..61985e56b769e 100644 --- a/tests/ui/infallible_destructuring_match.fixed +++ b/tests/ui/infallible_destructuring_match.fixed @@ -9,6 +9,9 @@ enum SingleVariantEnum { struct TupleStruct(i32); +struct NonCopy; +struct TupleStructWithNonCopy(NonCopy); + enum EmptyEnum {} macro_rules! match_enum { @@ -71,6 +74,15 @@ fn infallible_destructuring_match_struct() { let TupleStruct(data) = wrapper; } +fn infallible_destructuring_match_struct_with_noncopy() { + let wrapper = TupleStructWithNonCopy(NonCopy); + + // This should lint! (keeping `ref` in the suggestion) + let TupleStructWithNonCopy(ref data) = wrapper; + + let TupleStructWithNonCopy(ref data) = wrapper; +} + macro_rules! match_never_enum { ($param:expr) => { let data = match $param { diff --git a/tests/ui/infallible_destructuring_match.rs b/tests/ui/infallible_destructuring_match.rs index 106cd438b90e7..f2768245bbc41 100644 --- a/tests/ui/infallible_destructuring_match.rs +++ b/tests/ui/infallible_destructuring_match.rs @@ -9,6 +9,9 @@ enum SingleVariantEnum { struct TupleStruct(i32); +struct NonCopy; +struct TupleStructWithNonCopy(NonCopy); + enum EmptyEnum {} macro_rules! match_enum { @@ -75,6 +78,17 @@ fn infallible_destructuring_match_struct() { let TupleStruct(data) = wrapper; } +fn infallible_destructuring_match_struct_with_noncopy() { + let wrapper = TupleStructWithNonCopy(NonCopy); + + // This should lint! (keeping `ref` in the suggestion) + let data = match wrapper { + TupleStructWithNonCopy(ref n) => n, + }; + + let TupleStructWithNonCopy(ref data) = wrapper; +} + macro_rules! match_never_enum { ($param:expr) => { let data = match $param { diff --git a/tests/ui/infallible_destructuring_match.stderr b/tests/ui/infallible_destructuring_match.stderr index 1b78db42014a2..f8a50f0223d63 100644 --- a/tests/ui/infallible_destructuring_match.stderr +++ b/tests/ui/infallible_destructuring_match.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` - --> $DIR/infallible_destructuring_match.rs:26:5 + --> $DIR/infallible_destructuring_match.rs:29:5 | LL | / let data = match wrapper { LL | | SingleVariantEnum::Variant(i) => i, @@ -9,7 +9,7 @@ LL | | }; = note: `-D clippy::infallible-destructuring-match` implied by `-D warnings` error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` - --> $DIR/infallible_destructuring_match.rs:58:5 + --> $DIR/infallible_destructuring_match.rs:61:5 | LL | / let data = match wrapper { LL | | TupleStruct(i) => i, @@ -17,12 +17,20 @@ LL | | }; | |______^ help: try this: `let TupleStruct(data) = wrapper;` error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` - --> $DIR/infallible_destructuring_match.rs:90:5 + --> $DIR/infallible_destructuring_match.rs:85:5 + | +LL | / let data = match wrapper { +LL | | TupleStructWithNonCopy(ref n) => n, +LL | | }; + | |______^ help: try this: `let TupleStructWithNonCopy(ref data) = wrapper;` + +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` + --> $DIR/infallible_destructuring_match.rs:104:5 | LL | / let data = match wrapper { LL | | Ok(i) => i, LL | | }; | |______^ help: try this: `let Ok(data) = wrapper;` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors From f75fc857839ff71e89d968bb1952e54a3969fb9f Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 15 Nov 2022 18:24:18 +0000 Subject: [PATCH 141/169] Extend `needless_borrowed_reference` to structs and tuples, ignore _ --- clippy_lints/src/enum_variants.rs | 2 +- clippy_lints/src/int_plus_one.rs | 28 ++--- clippy_lints/src/len_zero.rs | 3 +- clippy_lints/src/needless_borrowed_ref.rs | 108 +++++++++++------ clippy_lints/src/unsafe_removed_from_name.rs | 2 +- clippy_utils/src/consts.rs | 22 ++-- clippy_utils/src/hir_utils.rs | 16 ++- tests/ui/match_expr_like_matches_macro.fixed | 7 +- tests/ui/match_expr_like_matches_macro.rs | 7 +- tests/ui/match_expr_like_matches_macro.stderr | 28 ++--- tests/ui/needless_borrowed_ref.fixed | 67 ++++++++++- tests/ui/needless_borrowed_ref.rs | 67 ++++++++++- tests/ui/needless_borrowed_ref.stderr | 113 ++++++++++++++++-- 13 files changed, 356 insertions(+), 114 deletions(-) diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index b019d07d53d11..7add358e8e4ee 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -250,7 +250,7 @@ impl LateLintPass<'_> for EnumVariantNames { let item_name = item.ident.name.as_str(); let item_camel = to_camel_case(item_name); if !item.span.from_expansion() && is_present_in_source(cx, item.span) { - if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() { + if let Some((mod_name, mod_camel)) = self.modules.last() { // constants don't have surrounding modules if !mod_camel.is_empty() { if mod_name == &item.ident.name { diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 33491da3fc5af..51757592c02d7 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -62,58 +62,54 @@ impl IntPlusOne { fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option { match (binop, &lhs.kind, &rhs.kind) { // case where `x - 1 >= ...` or `-1 + x >= ...` - (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => { + (BinOpKind::Ge, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) => { match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { // `-1 + x` - (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, // `x - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } }, // case where `... >= y + 1` or `... >= 1 + y` - (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) - if rhskind.node == BinOpKind::Add => - { + (BinOpKind::Ge, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) if rhskind.node == BinOpKind::Add => { match (&rhslhs.kind, &rhsrhs.kind) { // `y + 1` and `1 + y` - (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { + (ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, - (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (_, ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, } }, // case where `x + 1 <= ...` or `1 + x <= ...` - (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) - if lhskind.node == BinOpKind::Add => - { + (BinOpKind::Le, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) if lhskind.node == BinOpKind::Add => { match (&lhslhs.kind, &lhsrhs.kind) { // `1 + x` and `x + 1` - (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { + (ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, - (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (_, ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } }, // case where `... >= y - 1` or `... >= -1 + y` - (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => { + (BinOpKind::Le, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) => { match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { // `-1 + y` - (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, // `y - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 3a563736fb077..76b0c21327c87 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -366,8 +366,7 @@ fn check_for_is_empty<'tcx>( } fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { - if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) - { + if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(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" { diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index 10c3ff026b6d6..498e1408e52a0 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -36,14 +36,14 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]); impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { - fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - if pat.span.from_expansion() { + fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) { + if ref_pat.span.from_expansion() { // OK, simple enough, lints doesn't check in macro. return; } // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms - for (_, node) in cx.tcx.hir().parent_iter(pat.hir_id) { + for (_, node) in cx.tcx.hir().parent_iter(ref_pat.hir_id) { let Node::Pat(pat) = node else { break }; if matches!(pat.kind, PatKind::Or(_)) { @@ -52,20 +52,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { } // Only lint immutable refs, because `&mut ref T` may be useful. - let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind else { return }; + let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { return }; - match sub_pat.kind { + match pat.kind { // Check sub_pat got a `ref` keyword (excluding `ref mut`). PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { span_lint_and_then( cx, NEEDLESS_BORROWED_REFERENCE, - pat.span, + ref_pat.span, "this pattern takes a reference on something that is being dereferenced", |diag| { // `&ref ident` // ^^^^^ - let span = pat.span.until(ident.span); + let span = ref_pat.span.until(ident.span); diag.span_suggestion_verbose( span, "try removing the `&ref` part", @@ -84,41 +84,71 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { }), after, ) => { - let mut suggestions = Vec::new(); - - for element_pat in itertools::chain(before, after) { - if let PatKind::Binding(BindingAnnotation::REF, _, ident, None) = element_pat.kind { - // `&[..., ref ident, ...]` - // ^^^^ - let span = element_pat.span.until(ident.span); - suggestions.push((span, String::new())); - } else { - return; - } - } + check_subpatterns( + cx, + "dereferencing a slice pattern where every element takes a reference", + ref_pat, + pat, + itertools::chain(before, after), + ); + }, + PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => { + check_subpatterns( + cx, + "dereferencing a tuple pattern where every element takes a reference", + ref_pat, + pat, + subpatterns, + ); + }, + PatKind::Struct(_, fields, _) => { + check_subpatterns( + cx, + "dereferencing a struct pattern where every field's pattern takes a reference", + ref_pat, + pat, + fields.iter().map(|field| field.pat), + ); + }, + _ => {}, + } + } +} - if !suggestions.is_empty() { - span_lint_and_then( - cx, - NEEDLESS_BORROWED_REFERENCE, - pat.span, - "dereferencing a slice pattern where every element takes a reference", - |diag| { - // `&[...]` - // ^ - let span = pat.span.until(sub_pat.span); - suggestions.push((span, String::new())); +fn check_subpatterns<'tcx>( + cx: &LateContext<'tcx>, + message: &str, + ref_pat: &Pat<'_>, + pat: &Pat<'_>, + subpatterns: impl IntoIterator>, +) { + let mut suggestions = Vec::new(); - diag.multipart_suggestion( - "try removing the `&` and `ref` parts", - suggestions, - Applicability::MachineApplicable, - ); - }, - ); - } + for subpattern in subpatterns { + match subpattern.kind { + PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { + // `ref ident` + // ^^^^ + let span = subpattern.span.until(ident.span); + suggestions.push((span, String::new())); }, - _ => {}, + PatKind::Wild => {}, + _ => return, } } + + if !suggestions.is_empty() { + span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, ref_pat.span, message, |diag| { + // `&pat` + // ^ + let span = ref_pat.span.until(pat.span); + suggestions.push((span, String::new())); + + diag.multipart_suggestion( + "try removing the `&` and `ref` parts", + suggestions, + Applicability::MachineApplicable, + ); + }); + } } diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 32cd468120141..952586527689a 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -50,7 +50,7 @@ fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { }, UseTreeKind::Simple(None, ..) | UseTreeKind::Glob => {}, UseTreeKind::Nested(ref nested_use_tree) => { - for &(ref use_tree, _) in nested_use_tree { + for (use_tree, _) in nested_use_tree { check_use_tree(use_tree, cx, span); } }, diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index f01afda72b2c8..315aea9aa091b 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -51,8 +51,8 @@ pub enum Constant { impl PartialEq for Constant { fn eq(&self, other: &Self) -> bool { match (self, other) { - (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs, - (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r, + (Self::Str(ls), Self::Str(rs)) => ls == rs, + (Self::Binary(l), Self::Binary(r)) => l == r, (&Self::Char(l), &Self::Char(r)) => l == r, (&Self::Int(l), &Self::Int(r)) => l == r, (&Self::F64(l), &Self::F64(r)) => { @@ -69,8 +69,8 @@ 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, + (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => ls == rs && lv == rv, + (Self::Ref(lb), Self::Ref(rb)) => *lb == *rb, // TODO: are there inter-type equalities? _ => false, } @@ -126,8 +126,8 @@ impl Hash for Constant { impl Constant { pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { match (left, right) { - (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), - (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), + (Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)), + (Self::Char(l), Self::Char(r)) => Some(l.cmp(r)), (&Self::Int(l), &Self::Int(r)) => match *cmp_type.kind() { ty::Int(int_ty) => Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))), ty::Uint(_) => Some(l.cmp(&r)), @@ -135,8 +135,8 @@ impl Constant { }, (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), - (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)), - (&Self::Tuple(ref l), &Self::Tuple(ref r)) if l.len() == r.len() => match *cmp_type.kind() { + (Self::Bool(l), Self::Bool(r)) => Some(l.cmp(r)), + (Self::Tuple(l), Self::Tuple(r)) if l.len() == r.len() => match *cmp_type.kind() { ty::Tuple(tys) if tys.len() == l.len() => l .iter() .zip(r) @@ -146,7 +146,7 @@ impl Constant { .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), _ => None, }, - (&Self::Vec(ref l), &Self::Vec(ref r)) => { + (Self::Vec(l), Self::Vec(r)) => { let (ty::Array(cmp_type, _) | ty::Slice(cmp_type)) = *cmp_type.kind() else { return None }; @@ -155,7 +155,7 @@ impl Constant { .find(|r| r.map_or(true, |o| o != Ordering::Equal)) .unwrap_or_else(|| Some(l.len().cmp(&r.len()))) }, - (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { + (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => { match Self::partial_cmp( tcx, match *cmp_type.kind() { @@ -169,7 +169,7 @@ impl Constant { x => x, } }, - (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp( + (Self::Ref(lb), Self::Ref(rb)) => Self::partial_cmp( tcx, match *cmp_type.kind() { ty::Ref(_, ty, _) => ty, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index da8c976c952c9..0231a51adf482 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -266,7 +266,7 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::Let(l), &ExprKind::Let(r)) => { self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) }, - (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node, + (ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node, (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => { lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) }, @@ -291,8 +291,8 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => { self.eq_expr(le, re) && self.eq_array_length(ll, rl) }, - (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)), - (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r), + (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l, r, |l, r| self.eq_expr(l, r)), + (ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r), (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => { self.eq_qpath(l_path, r_path) && both(lo, ro, |l, r| self.eq_expr(l, r)) @@ -362,7 +362,7 @@ impl HirEqInterExpr<'_, '_, '_> { } eq }, - (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r), + (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { @@ -429,13 +429,11 @@ impl HirEqInterExpr<'_, '_, '_> { match (&left.kind, &right.kind) { (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl), - (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { - l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty) - }, - (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => { + (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty), + (TyKind::Rptr(_, l_rmut), TyKind::Rptr(_, r_rmut)) => { l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty) }, - (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r), + (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r), (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), (&TyKind::Infer, &TyKind::Infer) => true, _ => false, diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 2498007694c56..968f462f8a029 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -2,7 +2,12 @@ #![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] +#![allow( + unreachable_patterns, + dead_code, + clippy::equatable_if_let, + clippy::needless_borrowed_reference +)] fn main() { let x = Some(5); diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index b4e48499bd0fb..c6b479e27c5ac 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -2,7 +2,12 @@ #![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] +#![allow( + unreachable_patterns, + dead_code, + clippy::equatable_if_let, + clippy::needless_borrowed_reference +)] fn main() { let x = Some(5); diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index f1d1c23aeb0de..a4df8008ac239 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:11:14 + --> $DIR/match_expr_like_matches_macro.rs:16:14 | LL | let _y = match x { | ______________^ @@ -11,7 +11,7 @@ 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:17:14 + --> $DIR/match_expr_like_matches_macro.rs:22:14 | LL | let _w = match x { | ______________^ @@ -21,7 +21,7 @@ LL | | }; | |_____^ help: try this: `matches!(x, Some(_))` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:23:14 + --> $DIR/match_expr_like_matches_macro.rs:28:14 | LL | let _z = match x { | ______________^ @@ -33,7 +33,7 @@ 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:29:15 + --> $DIR/match_expr_like_matches_macro.rs:34:15 | LL | let _zz = match x { | _______________^ @@ -43,13 +43,13 @@ 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:35:16 + --> $DIR/match_expr_like_matches_macro.rs:40:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:59:20 + --> $DIR/match_expr_like_matches_macro.rs:64:20 | LL | let _ans = match x { | ____________________^ @@ -60,7 +60,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:69:20 + --> $DIR/match_expr_like_matches_macro.rs:74:20 | LL | let _ans = match x { | ____________________^ @@ -73,7 +73,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:79:20 + --> $DIR/match_expr_like_matches_macro.rs:84:20 | LL | let _ans = match x { | ____________________^ @@ -84,7 +84,7 @@ LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:139:18 + --> $DIR/match_expr_like_matches_macro.rs:144:18 | LL | let _z = match &z { | __________________^ @@ -94,7 +94,7 @@ LL | | }; | |_________^ help: try this: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:148:18 + --> $DIR/match_expr_like_matches_macro.rs:153:18 | LL | let _z = match &z { | __________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: try this: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:165:21 + --> $DIR/match_expr_like_matches_macro.rs:170:21 | LL | let _ = match &z { | _____________________^ @@ -114,7 +114,7 @@ LL | | }; | |_____________^ help: try this: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:179:20 + --> $DIR/match_expr_like_matches_macro.rs:184:20 | LL | let _res = match &val { | ____________________^ @@ -124,7 +124,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:191:20 + --> $DIR/match_expr_like_matches_macro.rs:196:20 | LL | let _res = match &val { | ____________________^ @@ -134,7 +134,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:251:14 + --> $DIR/match_expr_like_matches_macro.rs:256:14 | LL | let _y = match Some(5) { | ______________^ diff --git a/tests/ui/needless_borrowed_ref.fixed b/tests/ui/needless_borrowed_ref.fixed index bcb4eb2dd48a6..0c47ceb7b6791 100644 --- a/tests/ui/needless_borrowed_ref.fixed +++ b/tests/ui/needless_borrowed_ref.fixed @@ -1,11 +1,32 @@ // run-rustfix #![warn(clippy::needless_borrowed_reference)] -#![allow(unused, clippy::needless_borrow)] +#![allow( + unused, + irrefutable_let_patterns, + non_shorthand_field_patterns, + clippy::needless_borrow +)] fn main() {} -fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +struct Struct { + a: usize, + b: usize, + c: usize, +} + +struct TupleStruct(u8, u8, u8); + +fn should_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|a| a.is_empty()); @@ -24,16 +45,54 @@ fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec if let [a, b, ..] = slice {} if let [a, .., b] = slice {} if let [.., a, b] = slice {} + + if let [a, _] = slice {} + + if let (a, b, c) = &tuple {} + if let (a, _, c) = &tuple {} + if let (a, ..) = &tuple {} + + if let TupleStruct(a, ..) = &tuple_struct {} + + if let Struct { + a, + b: b, + c: renamed, + } = &s + {} + + if let Struct { a, b: _, .. } = &s {} } -fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +fn should_not_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { if let [ref a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} + if let &(ref a, b, ..) = &tuple {} + if let &TupleStruct(ref a, b, ..) = &tuple_struct {} + if let &Struct { ref a, b, .. } = &s {} + // must not be removed as variables must be bound consistently across | patterns if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} + // the `&`s here technically could be removed, but it'd be noisy and without a `ref` doesn't match + // the lint name + if let &[] = slice {} + if let &[_] = slice {} + if let &[..] = slice {} + if let &(..) = &tuple {} + if let &TupleStruct(..) = &tuple_struct {} + if let &Struct { .. } = &s {} + let mut var2 = 5; let thingy2 = Some(&mut var2); if let Some(&mut ref mut v) = thingy2 { @@ -59,6 +118,6 @@ fn foo(a: &Animal, b: &Animal) { // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted - (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted + (Animal::Dog(a), &Animal::Dog(_)) => (), } } diff --git a/tests/ui/needless_borrowed_ref.rs b/tests/ui/needless_borrowed_ref.rs index f6de1a6d83d1b..f883bb0c88917 100644 --- a/tests/ui/needless_borrowed_ref.rs +++ b/tests/ui/needless_borrowed_ref.rs @@ -1,11 +1,32 @@ // run-rustfix #![warn(clippy::needless_borrowed_reference)] -#![allow(unused, clippy::needless_borrow)] +#![allow( + unused, + irrefutable_let_patterns, + non_shorthand_field_patterns, + clippy::needless_borrow +)] fn main() {} -fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +struct Struct { + a: usize, + b: usize, + c: usize, +} + +struct TupleStruct(u8, u8, u8); + +fn should_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|&ref a| a.is_empty()); @@ -24,16 +45,54 @@ fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec if let &[ref a, ref b, ..] = slice {} if let &[ref a, .., ref b] = slice {} if let &[.., ref a, ref b] = slice {} + + if let &[ref a, _] = slice {} + + if let &(ref a, ref b, ref c) = &tuple {} + if let &(ref a, _, ref c) = &tuple {} + if let &(ref a, ..) = &tuple {} + + if let &TupleStruct(ref a, ..) = &tuple_struct {} + + if let &Struct { + ref a, + b: ref b, + c: ref renamed, + } = &s + {} + + if let &Struct { ref a, b: _, .. } = &s {} } -fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +fn should_not_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { if let [ref a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} + if let &(ref a, b, ..) = &tuple {} + if let &TupleStruct(ref a, b, ..) = &tuple_struct {} + if let &Struct { ref a, b, .. } = &s {} + // must not be removed as variables must be bound consistently across | patterns if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} + // the `&`s here technically could be removed, but it'd be noisy and without a `ref` doesn't match + // the lint name + if let &[] = slice {} + if let &[_] = slice {} + if let &[..] = slice {} + if let &(..) = &tuple {} + if let &TupleStruct(..) = &tuple_struct {} + if let &Struct { .. } = &s {} + let mut var2 = 5; let thingy2 = Some(&mut var2); if let Some(&mut ref mut v) = thingy2 { @@ -59,6 +118,6 @@ fn foo(a: &Animal, b: &Animal) { // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted - (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted + (Animal::Dog(a), &Animal::Dog(_)) => (), } } diff --git a/tests/ui/needless_borrowed_ref.stderr b/tests/ui/needless_borrowed_ref.stderr index 7453542e673f3..8d0f0c258dd26 100644 --- a/tests/ui/needless_borrowed_ref.stderr +++ b/tests/ui/needless_borrowed_ref.stderr @@ -1,5 +1,5 @@ error: this pattern takes a reference on something that is being dereferenced - --> $DIR/needless_borrowed_ref.rs:10:34 + --> $DIR/needless_borrowed_ref.rs:31:34 | LL | let _ = v.iter_mut().filter(|&ref a| a.is_empty()); | ^^^^^^ @@ -12,7 +12,7 @@ LL + let _ = v.iter_mut().filter(|a| a.is_empty()); | error: this pattern takes a reference on something that is being dereferenced - --> $DIR/needless_borrowed_ref.rs:14:17 + --> $DIR/needless_borrowed_ref.rs:35:17 | LL | if let Some(&ref v) = thingy {} | ^^^^^^ @@ -24,7 +24,7 @@ LL + if let Some(v) = thingy {} | error: this pattern takes a reference on something that is being dereferenced - --> $DIR/needless_borrowed_ref.rs:16:14 + --> $DIR/needless_borrowed_ref.rs:37:14 | LL | if let &[&ref a, ref b] = slice_of_refs {} | ^^^^^^ @@ -36,7 +36,7 @@ LL + if let &[a, ref b] = slice_of_refs {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:18:9 + --> $DIR/needless_borrowed_ref.rs:39:9 | LL | let &[ref a, ..] = &array; | ^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL + let [a, ..] = &array; | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:19:9 + --> $DIR/needless_borrowed_ref.rs:40:9 | LL | let &[ref a, ref b, ..] = &array; | ^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL + let [a, b, ..] = &array; | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:21:12 + --> $DIR/needless_borrowed_ref.rs:42:12 | LL | if let &[ref a, ref b] = slice {} | ^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL + if let [a, b] = slice {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:22:12 + --> $DIR/needless_borrowed_ref.rs:43:12 | LL | if let &[ref a, ref b] = &vec[..] {} | ^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL + if let [a, b] = &vec[..] {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:24:12 + --> $DIR/needless_borrowed_ref.rs:45:12 | LL | if let &[ref a, ref b, ..] = slice {} | ^^^^^^^^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL + if let [a, b, ..] = slice {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:25:12 + --> $DIR/needless_borrowed_ref.rs:46:12 | LL | if let &[ref a, .., ref b] = slice {} | ^^^^^^^^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL + if let [a, .., b] = slice {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:26:12 + --> $DIR/needless_borrowed_ref.rs:47:12 | LL | if let &[.., ref a, ref b] = slice {} | ^^^^^^^^^^^^^^^^^^^ @@ -119,5 +119,96 @@ LL - if let &[.., ref a, ref b] = slice {} LL + if let [.., a, b] = slice {} | -error: aborting due to 10 previous errors +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:49:12 + | +LL | if let &[ref a, _] = slice {} + | ^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, _] = slice {} +LL + if let [a, _] = slice {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:51:12 + | +LL | if let &(ref a, ref b, ref c) = &tuple {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &(ref a, ref b, ref c) = &tuple {} +LL + if let (a, b, c) = &tuple {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:52:12 + | +LL | if let &(ref a, _, ref c) = &tuple {} + | ^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &(ref a, _, ref c) = &tuple {} +LL + if let (a, _, c) = &tuple {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:53:12 + | +LL | if let &(ref a, ..) = &tuple {} + | ^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &(ref a, ..) = &tuple {} +LL + if let (a, ..) = &tuple {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:55:12 + | +LL | if let &TupleStruct(ref a, ..) = &tuple_struct {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &TupleStruct(ref a, ..) = &tuple_struct {} +LL + if let TupleStruct(a, ..) = &tuple_struct {} + | + +error: dereferencing a struct pattern where every field's pattern takes a reference + --> $DIR/needless_borrowed_ref.rs:57:12 + | +LL | if let &Struct { + | ____________^ +LL | | ref a, +LL | | b: ref b, +LL | | c: ref renamed, +LL | | } = &s + | |_____^ + | +help: try removing the `&` and `ref` parts + | +LL ~ if let Struct { +LL ~ a, +LL ~ b: b, +LL ~ c: renamed, + | + +error: dereferencing a struct pattern where every field's pattern takes a reference + --> $DIR/needless_borrowed_ref.rs:64:12 + | +LL | if let &Struct { ref a, b: _, .. } = &s {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &Struct { ref a, b: _, .. } = &s {} +LL + if let Struct { a, b: _, .. } = &s {} + | + +error: aborting due to 17 previous errors From f2d83ed1ac7254d943975fa57e54e6013d16eecf Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 10 Oct 2022 13:40:56 +1100 Subject: [PATCH 142/169] Use `token::Lit` in `ast::ExprKind::Lit`. Instead of `ast::Lit`. Literal lowering now happens at two different times. Expression literals are lowered when HIR is crated. Attribute literals are lowered during parsing. This commit changes the language very slightly. Some programs that used to not compile now will compile. This is because some invalid literals that are removed by `cfg` or attribute macros will no longer trigger errors. See this comment for more details: https://github.com/rust-lang/rust/pull/102944#issuecomment-1277476773 --- .../src/almost_complete_letter_range.rs | 19 +++++++--- clippy_lints/src/int_plus_one.rs | 23 ++++++------ clippy_lints/src/literal_representation.rs | 36 ++++++++++--------- clippy_lints/src/misc_early/literal_suffix.rs | 8 ++--- .../src/misc_early/mixed_case_hex_literals.rs | 6 ++-- clippy_lints/src/misc_early/mod.rs | 24 +++++++------ .../src/misc_early/zero_prefixed_literal.rs | 10 +++--- clippy_lints/src/octal_escapes.rs | 10 +++--- clippy_lints/src/precedence.rs | 5 +-- clippy_lints/src/unused_rounding.rs | 18 +++++----- clippy_utils/src/ast_utils.rs | 2 +- clippy_utils/src/numeric_literal.rs | 6 +--- 12 files changed, 90 insertions(+), 77 deletions(-) diff --git a/clippy_lints/src/almost_complete_letter_range.rs b/clippy_lints/src/almost_complete_letter_range.rs index 073e4af1318e3..df92579a85df2 100644 --- a/clippy_lints/src/almost_complete_letter_range.rs +++ b/clippy_lints/src/almost_complete_letter_range.rs @@ -73,12 +73,21 @@ impl EarlyLintPass for AlmostCompleteLetterRange { } fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) { - if let ExprKind::Lit(start_lit) = &start.peel_parens().kind - && let ExprKind::Lit(end_lit) = &end.peel_parens().kind + if let ExprKind::Lit(start_token_lit) = start.peel_parens().kind + && let ExprKind::Lit(end_token_lit) = end.peel_parens().kind && matches!( - (&start_lit.kind, &end_lit.kind), - (LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z')) - | (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z')) + ( + LitKind::from_token_lit(start_token_lit), + LitKind::from_token_lit(end_token_lit), + ), + ( + Ok(LitKind::Byte(b'a') | LitKind::Char('a')), + Ok(LitKind::Byte(b'z') | LitKind::Char('z')) + ) + | ( + Ok(LitKind::Byte(b'A') | LitKind::Char('A')), + Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')), + ) ) && !in_external_macro(cx.sess(), span) { diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 33491da3fc5af..f793abdfda34a 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -2,7 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind}; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind}; +use rustc_ast::token; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -52,8 +53,8 @@ enum Side { impl IntPlusOne { #[expect(clippy::cast_sign_loss)] - fn check_lit(lit: &Lit, target_value: i128) -> bool { - if let LitKind::Int(value, ..) = lit.kind { + fn check_lit(token_lit: token::Lit, target_value: i128) -> bool { + if let Ok(LitKind::Int(value, ..)) = LitKind::from_token_lit(token_lit) { return value == (target_value as u128); } false @@ -65,11 +66,11 @@ impl IntPlusOne { (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => { match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { // `-1 + x` - (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, // `x - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, @@ -81,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) => { + (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, - (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, @@ -96,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) => { + (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, - (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, @@ -109,11 +110,11 @@ impl IntPlusOne { (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => { match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { // `-1 + y` - (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, // `y - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 25f19b9c6e6c7..3a7b7835c990f 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -5,11 +5,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; use clippy_utils::source::snippet_opt; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; +use rustc_ast::ast::{Expr, ExprKind, LitKind}; +use rustc_ast::token; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; use std::iter; declare_clippy_lint! { @@ -236,8 +238,8 @@ impl EarlyLintPass for LiteralDigitGrouping { return; } - if let ExprKind::Lit(ref lit) = expr.kind { - self.check_lit(cx, lit); + if let ExprKind::Lit(lit) = expr.kind { + self.check_lit(cx, lit, expr.span); } } } @@ -252,12 +254,13 @@ impl LiteralDigitGrouping { } } - fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { 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(src) = snippet_opt(cx, span); + if let Ok(lit_kind) = LitKind::from_token_lit(lit); + if let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); then { - if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) { + if !Self::check_for_mistyped_suffix(cx, span, &mut num_lit) { return; } @@ -293,14 +296,14 @@ impl LiteralDigitGrouping { | WarningType::InconsistentDigitGrouping | WarningType::UnusualByteGroupings | WarningType::LargeDigitGroups => { - !lit.span.from_expansion() + !span.from_expansion() } WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => { true } }; if should_warn { - warning_type.display(num_lit.format(), cx, lit.span); + warning_type.display(num_lit.format(), cx, span); } } } @@ -458,8 +461,8 @@ impl EarlyLintPass for DecimalLiteralRepresentation { return; } - if let ExprKind::Lit(ref lit) = expr.kind { - self.check_lit(cx, lit); + if let ExprKind::Lit(lit) = expr.kind { + self.check_lit(cx, lit, expr.span); } } } @@ -469,19 +472,20 @@ impl DecimalLiteralRepresentation { pub fn new(threshold: u64) -> Self { Self { threshold } } - fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // Lint integral literals. 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 Ok(lit_kind) = LitKind::from_token_lit(lit); + if let LitKind::Int(val, _) = lit_kind; + if let Some(src) = snippet_opt(cx, span); + if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); if num_lit.radix == Radix::Decimal; if val >= u128::from(self.threshold); then { let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| { - warning_type.display(num_lit.format(), cx, lit.span); + warning_type.display(num_lit.format(), cx, span); }); } } diff --git a/clippy_lints/src/misc_early/literal_suffix.rs b/clippy_lints/src/misc_early/literal_suffix.rs index 27e7f8505eb5b..eda4376f200ee 100644 --- a/clippy_lints/src/misc_early/literal_suffix.rs +++ b/clippy_lints/src/misc_early/literal_suffix.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::Lit; use rustc_errors::Applicability; use rustc_lint::EarlyContext; +use rustc_span::Span; use super::{SEPARATED_LITERAL_SUFFIX, UNSEPARATED_LITERAL_SUFFIX}; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str, suffix: &str, sugg_type: &str) { let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; @@ -15,7 +15,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s span_lint_and_sugg( cx, SEPARATED_LITERAL_SUFFIX, - lit.span, + lit_span, &format!("{sugg_type} type suffix should not be separated by an underscore"), "remove the underscore", format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]), @@ -25,7 +25,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s span_lint_and_sugg( cx, UNSEPARATED_LITERAL_SUFFIX, - lit.span, + lit_span, &format!("{sugg_type} type suffix should be separated by an underscore"), "add an underscore", format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]), diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index 263ee1e945a25..ddb8b9173a537 100644 --- a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast::Lit; use rustc_lint::EarlyContext; +use rustc_span::Span; use super::MIXED_CASE_HEX_LITERALS; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, suffix: &str, lit_snip: &str) { let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; @@ -23,7 +23,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &s span_lint( cx, MIXED_CASE_HEX_LITERALS, - lit.span, + lit_span, "inconsistent casing in hexadecimal literal", ); break; diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index c8227ca445057..78be6b9e23fa2 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -9,7 +9,8 @@ mod zero_prefixed_literal; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; +use rustc_ast::ast::{Expr, ExprKind, Generics, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; +use rustc_ast::token; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; @@ -374,42 +375,43 @@ impl EarlyLintPass for MiscEarlyLints { return; } - if let ExprKind::Lit(ref lit) = expr.kind { - MiscEarlyLints::check_lit(cx, lit); + if let ExprKind::Lit(lit) = expr.kind { + MiscEarlyLints::check_lit(cx, lit, expr.span); } double_neg::check(cx, expr); } } impl MiscEarlyLints { - fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // We test if first character in snippet is a number, because the snippet could be an expansion // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`. // Note that this check also covers special case that `line!()` is eagerly expanded by compiler. // See for a regression. // FIXME: Find a better way to detect those cases. - let lit_snip = match snippet_opt(cx, lit.span) { + let lit_snip = match snippet_opt(cx, span) { Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip, _ => return, }; - if let LitKind::Int(value, lit_int_type) = lit.kind { + let lit_kind = LitKind::from_token_lit(lit); + if let Ok(LitKind::Int(value, lit_int_type)) = lit_kind { let suffix = match lit_int_type { LitIntType::Signed(ty) => ty.name_str(), LitIntType::Unsigned(ty) => ty.name_str(), LitIntType::Unsuffixed => "", }; - literal_suffix::check(cx, lit, &lit_snip, suffix, "integer"); + literal_suffix::check(cx, span, &lit_snip, suffix, "integer"); if lit_snip.starts_with("0x") { - mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip); + mixed_case_hex_literals::check(cx, span, suffix, &lit_snip); } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { // nothing to do } else if value != 0 && lit_snip.starts_with('0') { - zero_prefixed_literal::check(cx, lit, &lit_snip); + zero_prefixed_literal::check(cx, span, &lit_snip); } - } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { + } else if let Ok(LitKind::Float(_, LitFloatType::Suffixed(float_ty))) = lit_kind { let suffix = float_ty.name_str(); - literal_suffix::check(cx, lit, &lit_snip, suffix, "float"); + literal_suffix::check(cx, span, &lit_snip, suffix, "float"); } } } diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 9ead43ea4a477..4f9578d1b2576 100644 --- a/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -1,20 +1,20 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::ast::Lit; use rustc_errors::Applicability; use rustc_lint::EarlyContext; +use rustc_span::Span; use super::ZERO_PREFIXED_LITERAL; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) { let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0'); span_lint_and_then( cx, ZERO_PREFIXED_LITERAL, - lit.span, + lit_span, "this is a decimal constant", |diag| { diag.span_suggestion( - lit.span, + lit_span, "if you mean to use a decimal constant, remove the `0` to avoid confusion", trimmed_lit_snip.to_string(), Applicability::MaybeIncorrect, @@ -22,7 +22,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { // do not advise to use octal form if the literal cannot be expressed in base 8. if !lit_snip.contains(|c| c == '8' || c == '9') { diag.span_suggestion( - lit.span, + lit_span, "if you mean to use an octal constant, use `0o`", format!("0o{trimmed_lit_snip}"), Applicability::MaybeIncorrect, diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index f380a5065827d..2a7159764e463 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -56,11 +56,11 @@ impl EarlyLintPass for OctalEscapes { return; } - if let ExprKind::Lit(lit) = &expr.kind { - if matches!(lit.token_lit.kind, LitKind::Str) { - check_lit(cx, &lit.token_lit, lit.span, true); - } else if matches!(lit.token_lit.kind, LitKind::ByteStr) { - check_lit(cx, &lit.token_lit, lit.span, false); + if let ExprKind::Lit(token_lit) = &expr.kind { + if matches!(token_lit.kind, LitKind::Str) { + check_lit(cx, &token_lit, expr.span, true); + } else if matches!(token_lit.kind, LitKind::ByteStr) { + check_lit(cx, &token_lit, expr.span, false); } } } diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index e6e3ad05ad70a..bee4a33fb4a01 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; -use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_ast::token; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -120,7 +121,7 @@ impl EarlyLintPass for Precedence { if_chain! { if !all_odd; if let ExprKind::Lit(lit) = &arg.kind; - if let LitKind::Int(..) | LitKind::Float(..) = &lit.kind; + if let token::LitKind::Integer | token::LitKind::Float = &lit.kind; then { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 3164937293b68..3c1998d0237d9 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind}; +use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -33,14 +33,14 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { if let ExprKind::MethodCall(name_ident, receiver, _, _) = &expr.kind && let method_name = name_ident.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") - && let ExprKind::Lit(spanned) = &receiver.kind - && let LitKind::Float(symbol, ty) = spanned.kind { - let f = symbol.as_str().parse::().unwrap(); - let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { - ty.name_str() - } else { - "" - }; + && let ExprKind::Lit(token_lit) = &receiver.kind + && token_lit.is_semantic_float() { + let f = token_lit.symbol.as_str().parse::().unwrap(); + let mut f_str = token_lit.symbol.to_string(); + match token_lit.suffix { + Some(suffix) => f_str.push_str(suffix.as_str()), + None => {} + } if f.fract() == 0.0 { Some((method_name, f_str)) } else { diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 0133997560eae..73d1ba727c821 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -152,7 +152,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { }, (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, + (Lit(l), Lit(r)) => l == r, (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), diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index c5dcd7b31f58e..42bdfd4827f10 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -1,4 +1,4 @@ -use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; +use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use std::iter; #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -46,10 +46,6 @@ pub struct NumericLiteral<'a> { } impl<'a> NumericLiteral<'a> { - pub fn from_lit(src: &'a str, lit: &Lit) -> Option> { - NumericLiteral::from_lit_kind(src, &lit.kind) - } - pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option> { let unsigned_src = src.strip_prefix('-').map_or(src, |s| s); if lit_kind.is_numeric() From 9f3b6e9acd367b53b80031af7e8b61226b1eaabc Mon Sep 17 00:00:00 2001 From: Kartavya Vashishtha Date: Wed, 16 Nov 2022 11:01:07 +0530 Subject: [PATCH 143/169] don't emit AlwaysBreaks if it targets a block Introduced an ignored_ids parameter. Takes O(n^2) time in the worst case. Can be changed to collect block ids in first phase, and then filter with binary search in second. --- clippy_lints/src/loops/never_loop.rs | 113 ++++++++++++++++----------- tests/ui/never_loop.rs | 11 ++- 2 files changed, 79 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index abb18187ef147..123e1e3cce15a 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; +use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_span::Span; use std::iter::{once, Iterator}; @@ -16,7 +16,7 @@ pub(super) fn check( span: Span, for_loop: Option<&ForLoop<'_>>, ) { - match never_loop_block(block, loop_id) { + match never_loop_block(block, &mut Vec::new(), loop_id) { NeverLoopResult::AlwaysBreak => { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { if let Some(ForLoop { @@ -92,35 +92,33 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult } } -fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { - let mut iter = block +fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec, main_loop_id: HirId) -> NeverLoopResult { + let iter = block .stmts .iter() .filter_map(stmt_to_expr) .chain(block.expr.map(|expr| (expr, None))); - never_loop_expr_seq(&mut iter, main_loop_id) -} -fn never_loop_expr_seq<'a, T: Iterator, Option<&'a Block<'a>>)>>( - es: &mut T, - main_loop_id: HirId, -) -> NeverLoopResult { - es.map(|(e, els)| { - let e = never_loop_expr(e, main_loop_id); - els.map_or(e, |els| combine_branches(e, never_loop_block(els, main_loop_id))) + iter.map(|(e, els)| { + let e = never_loop_expr(e, ignore_ids, main_loop_id); + // els is an else block in a let...else binding + els.map_or(e, |els| { + combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id)) + }) }) .fold(NeverLoopResult::Otherwise, combine_seq) } fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> { match stmt.kind { - StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)), + StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)), + // add the let...else expression (if present) StmtKind::Local(local) => local.init.map(|init| (init, local.els)), StmtKind::Item(..) => None, } } -fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { +fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec, main_loop_id: HirId) -> NeverLoopResult { match expr.kind { ExprKind::Box(e) | ExprKind::Unary(_, e) @@ -129,48 +127,56 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Repeat(e, _) - | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id), - ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id), - ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), main_loop_id), - ExprKind::MethodCall(_, receiver, es, _) => { - never_loop_expr_all(&mut std::iter::once(receiver).chain(es.iter()), main_loop_id) - }, + | ExprKind::DropTemps(e) => never_loop_expr(e, ignore_ids, main_loop_id), + ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, ignore_ids, main_loop_id), + ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), ignore_ids, main_loop_id), + ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all( + &mut std::iter::once(receiver).chain(es.iter()), + ignore_ids, + main_loop_id, + ), ExprKind::Struct(_, fields, base) => { - let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id); + let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id); if let Some(base) = base { - combine_both(fields, never_loop_expr(base, main_loop_id)) + combine_both(fields, never_loop_expr(base, ignore_ids, main_loop_id)) } else { fields } }, - ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id), + ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), ignore_ids, 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().copied(), main_loop_id), + | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), ignore_ids, 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)) + absorb_break(never_loop_block(b, ignore_ids, main_loop_id)) }, ExprKind::If(e, e2, e3) => { - let e1 = never_loop_expr(e, main_loop_id); - let e2 = never_loop_expr(e2, main_loop_id); - let e3 = e3 - .as_ref() - .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id)); + let e1 = never_loop_expr(e, ignore_ids, main_loop_id); + let e2 = never_loop_expr(e2, ignore_ids, main_loop_id); + let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| { + never_loop_expr(e, ignore_ids, main_loop_id) + }); combine_seq(e1, combine_branches(e2, e3)) }, ExprKind::Match(e, arms, _) => { - let e = never_loop_expr(e, main_loop_id); + let e = never_loop_expr(e, ignore_ids, main_loop_id); if arms.is_empty() { e } else { - let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id); + let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id); combine_seq(e, arms) } }, - ExprKind::Block(b, None) => never_loop_block(b, main_loop_id), - ExprKind::Block(b, Some(_label)) => absorb_break(never_loop_block(b, main_loop_id)), + ExprKind::Block(b, l) => { + if let Some(_) = l { + ignore_ids.push(b.hir_id); + } + let ret = never_loop_block(b, ignore_ids, main_loop_id); + ignore_ids.pop(); + ret + }, ExprKind::Continue(d) => { let id = d .target_id @@ -181,20 +187,31 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { NeverLoopResult::AlwaysBreak } }, + ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e + .map_or(NeverLoopResult::Otherwise, |e| { + combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise) + }), ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { - combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) + combine_seq( + never_loop_expr(e, ignore_ids, main_loop_id), + NeverLoopResult::AlwaysBreak, + ) }), ExprKind::InlineAsm(asm) => asm .operands .iter() .map(|(o, _)| match o { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { - never_loop_expr(expr, main_loop_id) + never_loop_expr(expr, ignore_ids, main_loop_id) }, - InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter().copied(), main_loop_id), - InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - never_loop_expr_all(&mut once(*in_expr).chain(out_expr.iter().copied()), main_loop_id) + InlineAsmOperand::Out { expr, .. } => { + never_loop_expr_all(&mut expr.iter().copied(), ignore_ids, main_loop_id) }, + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all( + &mut once(*in_expr).chain(out_expr.iter().copied()), + ignore_ids, + main_loop_id, + ), InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise, @@ -209,13 +226,21 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { } } -fn never_loop_expr_all<'a, T: Iterator>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { - es.map(|e| never_loop_expr(e, main_loop_id)) +fn never_loop_expr_all<'a, T: Iterator>>( + es: &mut T, + ignore_ids: &mut Vec, + main_loop_id: HirId, +) -> NeverLoopResult { + es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id)) .fold(NeverLoopResult::Otherwise, combine_both) } -fn never_loop_expr_branch<'a, T: Iterator>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult { - e.map(|e| never_loop_expr(e, main_loop_id)) +fn never_loop_expr_branch<'a, T: Iterator>>( + e: &mut T, + ignore_ids: &mut Vec, + main_loop_id: HirId, +) -> NeverLoopResult { + e.map(|e| never_loop_expr(e, ignore_ids, main_loop_id)) .fold(NeverLoopResult::AlwaysBreak, combine_branches) } diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 86a5d03f765f6..28e8f459d4429 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -234,13 +234,22 @@ pub fn test19() { fn thing(iter: impl Iterator) { for _ in iter { 'b: { - // error goes away if we just have the block's value be (). break 'b; } } } } +pub fn test20() { + 'a: loop { + 'b: { + break 'b 'c: { + break 'a; + }; + } + } +} + fn main() { test1(); test2(); From 036a0108ac51a25ad4c51abd8182cc512b35e88a Mon Sep 17 00:00:00 2001 From: Kartavya Vashishtha Date: Wed, 16 Nov 2022 11:39:09 +0530 Subject: [PATCH 144/169] update tests --- tests/ui/never_loop.stderr | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 3033f019244a8..b7029bf8bed47 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -114,5 +114,17 @@ LL | | break x; LL | | }; | |_____^ -error: aborting due to 10 previous errors +error: this loop never actually loops + --> $DIR/never_loop.rs:244:5 + | +LL | / 'a: loop { +LL | | 'b: { +LL | | break 'b 'c: { +LL | | break 'a; +LL | | }; +LL | | } +LL | | } + | |_____^ + +error: aborting due to 11 previous errors From bc3cd344a1edefdea8eef7962d75bb35cdeea880 Mon Sep 17 00:00:00 2001 From: Kartavya Vashishtha Date: Wed, 16 Nov 2022 13:15:22 +0530 Subject: [PATCH 145/169] fix clippy suggestions --- clippy_lints/src/loops/never_loop.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 123e1e3cce15a..14f161f510265 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -118,6 +118,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t } } +#[allow(clippy::too_many_lines)] fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec, main_loop_id: HirId) -> NeverLoopResult { match expr.kind { ExprKind::Box(e) @@ -170,7 +171,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec, main_loop_id: H } }, ExprKind::Block(b, l) => { - if let Some(_) = l { + if l.is_some() { ignore_ids.push(b.hir_id); } let ret = never_loop_block(b, ignore_ids, main_loop_id); @@ -187,6 +188,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec, main_loop_id: H NeverLoopResult::AlwaysBreak } }, + // checks if break targets a block instead of a loop ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e .map_or(NeverLoopResult::Otherwise, |e| { combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise) From 2bf87f383026e68bda528017906f0bee89fe34d0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 15 Nov 2022 12:06:20 +0100 Subject: [PATCH 146/169] cleanup and dedupe CTFE and Miri error reporting --- tests/ui/indexing_slicing_index.stderr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index da5bc38b3b66c..d8b6e3f1262b7 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -4,11 +4,11 @@ error[E0080]: evaluation of `main::{constant#3}` failed LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 -error[E0080]: erroneous constant used +note: erroneous constant used --> $DIR/indexing_slicing_index.rs:31:5 | LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. - | ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + | ^^^^^^^^^^^^^^^^^^^^^^ error: indexing may panic --> $DIR/indexing_slicing_index.rs:22:5 @@ -65,6 +65,6 @@ error[E0080]: evaluation of constant value failed LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0080`. From e5352c72c748e6f585f1b0509fa51f1acc0bc7d4 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 9 Nov 2022 10:49:28 +0000 Subject: [PATCH 147/169] Convert predicates into Predicate in the Obligation constructor --- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/ptr.rs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index a37ee82d4c8ac..218dbeaddcade 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1156,7 +1156,7 @@ fn needless_borrow_impl_arg_position<'tcx>( } let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty); - let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); let infcx = cx.tcx.infer_ctxt().build(); infcx.predicate_must_hold_modulo_regions(&obligation) }) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 642a64ae77b6d..c7775313ecd02 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -419,7 +419,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< if trait_predicates.any(|predicate| { let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst); - let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation) }) { return false; diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 0d74c90a834f7..c8c6f32c6c989 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -695,6 +695,7 @@ fn matches_preds<'tcx>( .type_implements_trait(p.def_id, ty, p.substs, cx.param_env) .must_apply_modulo_regions(), ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new( + cx.tcx, ObligationCause::dummy(), cx.param_env, cx.tcx.mk_predicate(Binder::bind_with_vars( From 3f015a363020d3811e1f028c9ce4b0705c728289 Mon Sep 17 00:00:00 2001 From: koka Date: Thu, 17 Nov 2022 00:02:22 +0900 Subject: [PATCH 148/169] Avoid generating files via doctest When we run `cargo test` in `clippy_lints` directory, it will generate `foo.txt` in the directory. In order to avoid that, add `no_run` to rustdoc which contains `File::create`. --- clippy_lints/foo.txt | 1 - clippy_lints/src/methods/mod.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 clippy_lints/foo.txt diff --git a/clippy_lints/foo.txt b/clippy_lints/foo.txt deleted file mode 100644 index 5ab2f8a4323ab..0000000000000 --- a/clippy_lints/foo.txt +++ /dev/null @@ -1 +0,0 @@ -Hello \ No newline at end of file diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 43507f17e355f..acb3b20a61b5b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3077,7 +3077,7 @@ declare_clippy_lint! { /// /// ### Example /// - /// ```rust + /// ```rust,no_run /// use std::fs::File; /// use std::io::{self, Write, Seek, SeekFrom}; /// @@ -3090,7 +3090,7 @@ declare_clippy_lint! { /// } /// ``` /// Use instead: - /// ```rust + /// ```rust,no_run /// use std::fs::File; /// use std::io::{self, Write, Seek, SeekFrom}; /// From 333b92c5ed7c4a6849bcf72d0b026de64fde8df9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 8 Sep 2022 10:52:51 +1000 Subject: [PATCH 149/169] Box `ExprKind::{Closure,MethodCall}`, and `QSelf` in expressions, types, and patterns. --- clippy_lints/src/double_parens.rs | 8 ++--- clippy_lints/src/option_env_unwrap.rs | 6 ++-- clippy_lints/src/precedence.rs | 8 ++--- clippy_lints/src/redundant_closure_call.rs | 12 +++---- .../src/suspicious_operation_groupings.rs | 4 +-- clippy_lints/src/unnested_or_patterns.rs | 2 +- clippy_lints/src/unused_rounding.rs | 6 ++-- clippy_utils/src/ast_utils.rs | 32 ++++++++++++++++--- 8 files changed, 50 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 0f1d701865e7d..29425b2e55417 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -61,10 +61,10 @@ impl EarlyLintPass for DoubleParens { } } }, - ExprKind::MethodCall(_, _, ref params, _) => { - if let [ref param] = params[..] { - if let ExprKind::Paren(_) = param.kind { - span_lint(cx, DOUBLE_PARENS, param.span, msg); + ExprKind::MethodCall(ref call) => { + if let [ref arg] = call.args[..] { + if let ExprKind::Paren(_) = arg.kind { + span_lint(cx, DOUBLE_PARENS, arg.span, msg); } } }, diff --git a/clippy_lints/src/option_env_unwrap.rs b/clippy_lints/src/option_env_unwrap.rs index d9ee031c9f975..377bddeaa5fea 100644 --- a/clippy_lints/src/option_env_unwrap.rs +++ b/clippy_lints/src/option_env_unwrap.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_direct_expn_of; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind}; +use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -37,8 +37,8 @@ declare_lint_pass!(OptionEnvUnwrap => [OPTION_ENV_UNWRAP]); impl EarlyLintPass for OptionEnvUnwrap { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if_chain! { - if let ExprKind::MethodCall(path_segment, receiver, _, _) = &expr.kind; - if matches!(path_segment.ident.name, sym::expect | sym::unwrap); + if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind; + if matches!(seg.ident.name, sym::expect | sym::unwrap); if let ExprKind::Call(caller, _) = &receiver.kind; if is_direct_expn_of(caller.span, "option_env").is_some(); then { diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index bee4a33fb4a01..057b7e30642ec 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; -use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, MethodCall, UnOp}; use rustc_ast::token; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -110,11 +110,11 @@ impl EarlyLintPass for Precedence { let mut arg = operand; let mut all_odd = true; - while let ExprKind::MethodCall(path_segment, receiver, _, _) = &arg.kind { - let path_segment_str = path_segment.ident.name.as_str(); + while let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &arg.kind { + let seg_str = seg.ident.name.as_str(); all_odd &= ALLOWED_ODD_FUNCTIONS .iter() - .any(|odd_function| **odd_function == *path_segment_str); + .any(|odd_function| **odd_function == *seg_str); arg = receiver; } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 74eea6de4bbef..4cbe9597c5393 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -69,10 +69,10 @@ impl EarlyLintPass for RedundantClosureCall { 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 r#async, _, ref decl, ref block, _) = closure.kind; + if let ast::ExprKind::Closure(box ast::Closure { ref asyncness, ref fn_decl, ref body, .. }) = closure.kind; then { let mut visitor = ReturnVisitor::new(); - visitor.visit_expr(block); + visitor.visit_expr(body); if !visitor.found_return { span_lint_and_then( cx, @@ -80,13 +80,13 @@ impl EarlyLintPass for RedundantClosureCall { expr.span, "try not to call a closure in the expression where it is declared", |diag| { - if decl.inputs.is_empty() { + if fn_decl.inputs.is_empty() { let app = Applicability::MachineApplicable; - let mut hint = Sugg::ast(cx, block, ".."); + let mut hint = Sugg::ast(cx, body, ".."); - if r#async.is_async() { + if asyncness.is_async() { // `async x` is a syntax error, so it becomes `async { x }` - if !matches!(block.kind, ast::ExprKind::Block(_, _)) { + if !matches!(body.kind, ast::ExprKind::Block(_, _)) { hint = hint.blockify(); } diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index eef9bdc784940..78e83880e1a61 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -580,7 +580,7 @@ fn ident_difference_expr_with_base_location( | (Await(_), Await(_)) | (Async(_, _, _), Async(_, _, _)) | (Block(_, _), Block(_, _)) - | (Closure(_, _, _, _, _, _, _), Closure(_, _, _, _, _, _, _)) + | (Closure(_), Closure(_)) | (Match(_, _), Match(_, _)) | (Loop(_, _), Loop(_, _)) | (ForLoop(_, _, _, _), ForLoop(_, _, _, _)) @@ -593,7 +593,7 @@ fn ident_difference_expr_with_base_location( | (Unary(_, _), Unary(_, _)) | (Binary(_, _, _), Binary(_, _, _)) | (Tup(_), Tup(_)) - | (MethodCall(_, _, _, _), MethodCall(_, _, _, _)) + | (MethodCall(_), MethodCall(_)) | (Call(_, _), Call(_, _)) | (ConstBlock(_), ConstBlock(_)) | (Array(_), Array(_)) diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index b305dae76084c..bb6fb38e9690e 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -292,7 +292,7 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) /// 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( - qself1: &Option, + qself1: &Option>, path1: &ast::Path, fps1: &mut [ast::PatField], rest1: bool, diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 3c1998d0237d9..5ab351bc29ca0 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::{Expr, ExprKind}; +use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -30,8 +30,8 @@ declare_clippy_lint! { declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { - if let ExprKind::MethodCall(name_ident, receiver, _, _) = &expr.kind - && let method_name = name_ident.ident.name.as_str() + if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind + && let method_name = seg.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") && let ExprKind::Lit(token_lit) = &receiver.kind && token_lit.is_semantic_float() { diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 73d1ba727c821..23aed4b5ba2f4 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -75,11 +75,11 @@ pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool { && over(&l.attrs, &r.attrs, eq_attr) } -pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool { +pub fn eq_qself(l: &P, r: &P) -> bool { l.position == r.position && eq_ty(&l.ty, &r.ty) } -pub fn eq_maybe_qself(l: &Option, r: &Option) -> bool { +pub fn eq_maybe_qself(l: &Option>, r: &Option>) -> bool { match (l, r) { (Some(l), Some(r)) => eq_qself(l, r), (None, None) => true, @@ -147,8 +147,11 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (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, ls, la, _), MethodCall(rc, rs, ra, _)) => { - eq_path_seg(lc, rc) && eq_expr(ls, rs) && over(la, ra, |l, r| eq_expr(l, r)) + ( + MethodCall(box ast::MethodCall { seg: ls, receiver: lr, args: la, .. }), + MethodCall(box ast::MethodCall { seg: rs, receiver: rr, args: ra, .. }) + ) => { + eq_path_seg(ls, rs) && eq_expr(lr, rr) && 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), @@ -170,7 +173,26 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (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, eq_arm), - (Closure(lb, lc, la, lm, lf, le, _), Closure(rb, rc, ra, rm, rf, re, _)) => { + ( + Closure(box ast::Closure { + binder: lb, + capture_clause: lc, + asyncness: la, + movability: lm, + fn_decl: lf, + body: le, + .. + }), + Closure(box ast::Closure { + binder: rb, + capture_clause: rc, + asyncness: ra, + movability: rm, + fn_decl: rf, + body: re, + .. + }) + ) => { eq_closure_binder(lb, rb) && lc == rc && la.is_async() == ra.is_async() From 00ae5e15a8f2fde216a09751454f5e98cf241f21 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 17 Nov 2022 14:57:39 +0000 Subject: [PATCH 150/169] Fix typo in `expect_used` and `unwrap_used` warning messages --- clippy_lints/src/methods/expect_used.rs | 6 ++--- clippy_lints/src/methods/unwrap_used.rs | 6 ++--- tests/ui-toml/expect_used/expect_used.stderr | 4 +-- tests/ui-toml/unwrap_used/unwrap_used.stderr | 26 ++++++++++---------- tests/ui/expect.stderr | 6 ++--- tests/ui/get_unwrap.stderr | 26 ++++++++++---------- tests/ui/unwrap.stderr | 6 ++--- tests/ui/unwrap_expect_used.stderr | 12 ++++----- 8 files changed, 46 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index 0d3c89280465b..cce8f797e98c6 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -18,9 +18,9 @@ pub(super) fn check( let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { - Some((EXPECT_USED, "an Option", "None", "")) + Some((EXPECT_USED, "an `Option`", "None", "")) } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) + Some((EXPECT_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an ")) } else { None }; @@ -36,7 +36,7 @@ pub(super) fn check( cx, lint, expr.span, - &format!("used `{method}()` on `{kind}` value"), + &format!("used `{method}()` on {kind} value"), None, &format!("if this value is {none_prefix}`{none_value}`, it will panic"), ); diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index d11830a24b6c0..90983f249cd59 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -18,9 +18,9 @@ pub(super) fn check( let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { - Some((UNWRAP_USED, "an Option", "None", "")) + Some((UNWRAP_USED, "an `Option`", "None", "")) } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) + Some((UNWRAP_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an ")) } else { None }; @@ -45,7 +45,7 @@ pub(super) fn check( cx, lint, expr.span, - &format!("used `unwrap{method_suffix}()` on `{kind}` value"), + &format!("used `unwrap{method_suffix}()` on {kind} value"), None, &help, ); diff --git a/tests/ui-toml/expect_used/expect_used.stderr b/tests/ui-toml/expect_used/expect_used.stderr index 28a08599c67c6..1e9bb48c333ca 100644 --- a/tests/ui-toml/expect_used/expect_used.stderr +++ b/tests/ui-toml/expect_used/expect_used.stderr @@ -1,4 +1,4 @@ -error: used `expect()` on `an Option` value +error: used `expect()` on an `Option` value --> $DIR/expect_used.rs:6:13 | LL | let _ = opt.expect(""); @@ -7,7 +7,7 @@ LL | let _ = opt.expect(""); = help: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` -error: used `expect()` on `a Result` value +error: used `expect()` on a `Result` value --> $DIR/expect_used.rs:11:13 | LL | let _ = res.expect(""); diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr index 2bca88660e1c6..94b5ef663add9 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); @@ -25,7 +25,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); @@ -39,7 +39,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); @@ -53,7 +53,7 @@ error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); @@ -67,7 +67,7 @@ error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); @@ -81,7 +81,7 @@ error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); @@ -95,7 +95,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:44:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); @@ -109,7 +109,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:49:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; @@ -123,7 +123,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:50:10 | LL | *some_slice.get_mut(0).unwrap() = 1; @@ -137,7 +137,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:51:10 | LL | *some_vec.get_mut(0).unwrap() = 1; @@ -151,7 +151,7 @@ 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: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:52:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; @@ -165,7 +165,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); @@ -179,7 +179,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); diff --git a/tests/ui/expect.stderr b/tests/ui/expect.stderr index f6738865cac12..c08e0dbbf7449 100644 --- a/tests/ui/expect.stderr +++ b/tests/ui/expect.stderr @@ -1,4 +1,4 @@ -error: used `expect()` on `an Option` value +error: used `expect()` on an `Option` value --> $DIR/expect.rs:5:13 | LL | let _ = opt.expect(""); @@ -7,7 +7,7 @@ LL | let _ = opt.expect(""); = help: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` -error: used `expect()` on `a Result` value +error: used `expect()` on a `Result` value --> $DIR/expect.rs:10:13 | LL | let _ = res.expect(""); @@ -15,7 +15,7 @@ LL | let _ = res.expect(""); | = help: if this value is an `Err`, it will panic -error: used `expect_err()` on `a Result` value +error: used `expect_err()` on a `Result` value --> $DIR/expect.rs:11:13 | LL | let _ = res.expect_err(""); diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index 937f859040831..6dee4d5b4b624 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); @@ -25,7 +25,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); @@ -39,7 +39,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); @@ -53,7 +53,7 @@ error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); @@ -67,7 +67,7 @@ error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); @@ -81,7 +81,7 @@ error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); @@ -95,7 +95,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:44:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); @@ -109,7 +109,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:49:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; @@ -123,7 +123,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:50:10 | LL | *some_slice.get_mut(0).unwrap() = 1; @@ -137,7 +137,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:51:10 | LL | *some_vec.get_mut(0).unwrap() = 1; @@ -151,7 +151,7 @@ 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: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:52:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; @@ -165,7 +165,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); @@ -179,7 +179,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); diff --git a/tests/ui/unwrap.stderr b/tests/ui/unwrap.stderr index e88d580f7bd28..d49bf2b322837 100644 --- a/tests/ui/unwrap.stderr +++ b/tests/ui/unwrap.stderr @@ -1,4 +1,4 @@ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap.rs:5:13 | LL | let _ = opt.unwrap(); @@ -7,7 +7,7 @@ LL | let _ = opt.unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message = note: `-D clippy::unwrap-used` implied by `-D warnings` -error: used `unwrap()` on `a Result` value +error: used `unwrap()` on a `Result` value --> $DIR/unwrap.rs:10:13 | LL | let _ = res.unwrap(); @@ -15,7 +15,7 @@ LL | let _ = res.unwrap(); | = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message -error: used `unwrap_err()` on `a Result` value +error: used `unwrap_err()` on a `Result` value --> $DIR/unwrap.rs:11:13 | LL | let _ = res.unwrap_err(); diff --git a/tests/ui/unwrap_expect_used.stderr b/tests/ui/unwrap_expect_used.stderr index 211d2be18342d..fe4ecef114535 100644 --- a/tests/ui/unwrap_expect_used.stderr +++ b/tests/ui/unwrap_expect_used.stderr @@ -1,4 +1,4 @@ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_expect_used.rs:23:5 | LL | Some(3).unwrap(); @@ -7,7 +7,7 @@ LL | Some(3).unwrap(); = help: if this value is `None`, it will panic = note: `-D clippy::unwrap-used` implied by `-D warnings` -error: used `expect()` on `an Option` value +error: used `expect()` on an `Option` value --> $DIR/unwrap_expect_used.rs:24:5 | LL | Some(3).expect("Hello world!"); @@ -16,7 +16,7 @@ LL | Some(3).expect("Hello world!"); = help: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` -error: used `unwrap()` on `a Result` value +error: used `unwrap()` on a `Result` value --> $DIR/unwrap_expect_used.rs:31:5 | LL | a.unwrap(); @@ -24,7 +24,7 @@ LL | a.unwrap(); | = help: if this value is an `Err`, it will panic -error: used `expect()` on `a Result` value +error: used `expect()` on a `Result` value --> $DIR/unwrap_expect_used.rs:32:5 | LL | a.expect("Hello world!"); @@ -32,7 +32,7 @@ LL | a.expect("Hello world!"); | = help: if this value is an `Err`, it will panic -error: used `unwrap_err()` on `a Result` value +error: used `unwrap_err()` on a `Result` value --> $DIR/unwrap_expect_used.rs:33:5 | LL | a.unwrap_err(); @@ -40,7 +40,7 @@ LL | a.unwrap_err(); | = help: if this value is an `Ok`, it will panic -error: used `expect_err()` on `a Result` value +error: used `expect_err()` on a `Result` value --> $DIR/unwrap_expect_used.rs:34:5 | LL | a.expect_err("Hello error!"); From 82afb1617938e7075021ba440f1bcc0812dc4a37 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 5 Nov 2022 15:08:37 +0100 Subject: [PATCH 151/169] Add variant_name function to `LangItem` Clippy has an internal lint that checks for the usage of hardcoded def paths and suggests to replace them with a lang or diagnostic item, if possible. This was implemented with a hack, by getting all the variants of the `LangItem` enum and then index into it with the position of the `LangItem` in the `items` list. This is no longer possible, because the `items` list can't be accessed anymore. --- .../src/utils/internal_lints/invalid_paths.rs | 12 +++++------ .../internal_lints/unnecessary_def_path.rs | 19 +++++------------- tests/ui-internal/unnecessary_def_path.fixed | 6 +++--- tests/ui-internal/unnecessary_def_path.stderr | 6 +++--- ...unnecessary_def_path_hardcoded_path.stderr | 20 +++++++++---------- 5 files changed, 27 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 25532dd4e2681..22a5aa5351ad5 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -79,22 +79,22 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { SimplifiedTypeGen::StrSimplifiedType, ] .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); - for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { - let lang_item_path = cx.get_def_path(*item_def_id); + .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied()); + for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) { + let lang_item_path = cx.get_def_path(item_def_id); if path_syms.starts_with(&lang_item_path) { if let [item] = &path_syms[lang_item_path.len()..] { if matches!( - cx.tcx.def_kind(*item_def_id), + cx.tcx.def_kind(item_def_id), DefKind::Mod | DefKind::Enum | DefKind::Trait ) { - for child in cx.tcx.module_children(*item_def_id) { + for child in cx.tcx.module_children(item_def_id) { if child.ident.name == *item { return true; } } } else { - for child in cx.tcx.associated_item_def_ids(*item_def_id) { + for child in cx.tcx.associated_item_def_ids(item_def_id) { if cx.tcx.item_name(*child) == *item { return true; } diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 2a028c8141fce..cfba7fa8791de 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -6,7 +6,7 @@ use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; @@ -91,7 +91,7 @@ impl UnnecessaryDefPath { #[allow(clippy::too_many_lines)] fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) { enum Item { - LangItem(Symbol), + LangItem(&'static str), DiagnosticItem(Symbol), } static PATHS: &[&[&str]] = &[ @@ -325,18 +325,9 @@ fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option, def_id: DefId) -> Option { - if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { - let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); - let item_name = cx - .tcx - .adt_def(lang_items) - .variants() - .iter() - .nth(lang_item) - .unwrap() - .name; - Some(item_name) +fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { + if let Some((lang_item, _)) = cx.tcx.lang_items().iter().find(|(_, id)| *id == def_id) { + Some(lang_item.variant_name()) } else { None } diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed index cbbb465230641..e474f370a5d17 100644 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -48,14 +48,14 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox); let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit); - let _ = cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did); + let _ = cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did); let _ = cx.tcx.is_diagnostic_item(sym::Option, did); - let _ = cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did); + let _ = cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did); let _ = is_trait_method(cx, expr, sym::AsRef); let _ = is_path_diagnostic_item(cx, expr, sym::Option); - let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id)); + let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id)); let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome); } diff --git a/tests/ui-internal/unnecessary_def_path.stderr b/tests/ui-internal/unnecessary_def_path.stderr index a99a8f71fa6a7..3ca29f099771b 100644 --- a/tests/ui-internal/unnecessary_def_path.stderr +++ b/tests/ui-internal/unnecessary_def_path.stderr @@ -57,7 +57,7 @@ error: use of a def path to a `LangItem` --> $DIR/unnecessary_def_path.rs:51:13 | LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)` error: use of a def path to a diagnostic item --> $DIR/unnecessary_def_path.rs:52:13 @@ -69,7 +69,7 @@ error: use of a def path to a `LangItem` --> $DIR/unnecessary_def_path.rs:53:13 | LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)` | = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead @@ -89,7 +89,7 @@ error: use of a def path to a `LangItem` --> $DIR/unnecessary_def_path.rs:58:13 | LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))` error: use of a def path to a `LangItem` --> $DIR/unnecessary_def_path.rs:59:13 diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index af46d87bf676e..2a240cc249b0c 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -1,10 +1,10 @@ -error: hardcoded path to a language item - --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 | -LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: convert all references to use `LangItem::DerefMut` + = help: convert all references to use `sym::Deref` = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` error: hardcoded path to a diagnostic item @@ -15,13 +15,13 @@ LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", | = help: convert all references to use `sym::deref_method` -error: hardcoded path to a diagnostic item - --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 +error: hardcoded path to a language item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 | -LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: convert all references to use `sym::Deref` + = help: convert all references to use `LangItem::DerefMut` error: aborting due to 3 previous errors From a09423f8c8f6376a4cf4225d7fddb92ddf1f65bb Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 5 Jul 2022 16:56:16 +0000 Subject: [PATCH 152/169] Rm diagnostic item, use lang item --- clippy_lints/src/format.rs | 2 +- clippy_lints/src/format_push_string.rs | 6 ++-- clippy_lints/src/from_str_radix_10.rs | 6 ++-- clippy_lints/src/inherent_to_string.rs | 6 ++-- clippy_lints/src/manual_retain.rs | 4 +-- clippy_lints/src/manual_string_new.rs | 2 +- .../src/matches/match_str_case_mismatch.rs | 8 ++--- .../src/methods/bytes_count_to_len.rs | 5 ++-- clippy_lints/src/methods/bytes_nth.rs | 7 ++--- ...se_sensitive_file_extension_comparisons.rs | 8 ++--- clippy_lints/src/methods/expect_fun_call.rs | 6 ++-- .../src/methods/inefficient_to_string.rs | 6 ++-- clippy_lints/src/methods/manual_str_repeat.rs | 6 ++-- clippy_lints/src/methods/no_effect_replace.rs | 7 ++--- clippy_lints/src/methods/repeat_once.rs | 7 ++--- clippy_lints/src/methods/search_is_some.rs | 4 +-- .../src/methods/string_extend_chars.rs | 7 ++--- clippy_lints/src/methods/unnecessary_join.rs | 8 ++--- clippy_lints/src/needless_pass_by_value.rs | 6 ++-- clippy_lints/src/ptr.rs | 2 +- clippy_lints/src/redundant_clone.rs | 6 ++-- clippy_lints/src/strings.rs | 8 ++--- clippy_lints/src/types/box_collection.rs | 29 ++++++++++--------- clippy_lints/src/types/rc_buffer.rs | 8 ++--- .../src/unnecessary_owned_empty_strings.rs | 7 ++--- clippy_utils/src/lib.rs | 15 ++++++++-- 26 files changed, 95 insertions(+), 91 deletions(-) diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index bc0c68f535a96..d0fab69496040 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { if format_args.format_string.parts == [kw::Empty]; if arg.format.is_default(); if match cx.typeck_results().expr_ty(value).peel_refs().kind() { - ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()), + ty::Adt(adt, _) => Some(adt.did()) == cx.tcx.lang_items().string(), ty::Str => true, _ => false, }; diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs index 9b9f1872bfc1d..68c5c3673fe15 100644 --- a/clippy_lints/src/format_push_string.rs +++ b/clippy_lints/src/format_push_string.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{match_def_path, paths, peel_hir_expr_refs}; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -41,7 +41,7 @@ declare_clippy_lint! { declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String) + is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) } fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id { diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index cf8b7acd66d22..74a60b6a0d24b 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_integer_literal; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind}; +use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -98,5 +98,5 @@ impl<'tcx> 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) || is_type_diagnostic_item(cx, ty, sym::str) + is_type_lang_item(cx, ty, LangItem::String) || is_type_diagnostic_item(cx, ty, sym::str) } diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 14a37f535b46c..aaecc4fa8f256 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_type_lang_item}; use clippy_utils::{return_ty, trait_ref_of_method}; use if_chain::if_chain; -use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind}; +use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { if impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })); // Check if return type is String - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::String); + if is_type_lang_item(cx, return_ty(cx, impl_item.hir_id()), LangItem::String); // Filters instances of to_string which are required by a trait if trait_ref_of_method(cx, impl_item.owner_id.def_id).is_none(); diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 6abbab278feb4..d6438ca7fec2a 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq}; use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; @@ -140,7 +140,7 @@ fn check_to_owned( && let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id) && match_def_path(cx, chars_expr_def_id, &paths::STR_CHARS) && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs() - && is_type_diagnostic_item(cx, ty, sym::String) + && is_type_lang_item(cx, ty, hir::LangItem::String) && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) { suggest(cx, parent_expr, left_expr, filter_expr); } diff --git a/clippy_lints/src/manual_string_new.rs b/clippy_lints/src/manual_string_new.rs index 6acfb2ae3471c..c20d7959fc4a2 100644 --- a/clippy_lints/src/manual_string_new.rs +++ b/clippy_lints/src/manual_string_new.rs @@ -44,7 +44,7 @@ impl LateLintPass<'_> for ManualStringNew { let ty = cx.typeck_results().expr_ty(expr); match ty.kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => { - if !cx.tcx.is_diagnostic_item(sym::String, adt_def.did()) { + if cx.tcx.lang_items().string() != Some(adt_def.did()) { return; } }, diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index 6647322caa37a..675a85ae5553a 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Arm, Expr, ExprKind, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span}; +use rustc_span::Span; use super::MATCH_STR_CASE_MISMATCH; @@ -59,7 +59,7 @@ impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> { if let Some(case_method) = get_case_method(segment_ident) { let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); - if is_type_diagnostic_item(self.cx, ty, sym::String) || ty.kind() == &ty::Str { + if is_type_lang_item(self.cx, ty, LangItem::String) || ty.kind() == &ty::Str { self.case_method = Some(case_method); return true; } diff --git a/clippy_lints/src/methods/bytes_count_to_len.rs b/clippy_lints/src/methods/bytes_count_to_len.rs index fcfc25b523dac..89aaad359d4ac 100644 --- a/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/clippy_lints/src/methods/bytes_count_to_len.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::sym; use super::BYTES_COUNT_TO_LEN; @@ -20,7 +19,7 @@ pub(super) fn check<'tcx>( if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id); if cx.tcx.type_of(impl_id).is_str(); let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs(); - if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String); + if ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String); then { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 2e96346be977e..d512cc4eeae12 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; -use rustc_hir::Expr; +use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; -use rustc_span::sym; use super::BYTES_NTH; @@ -12,7 +11,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let caller_type = if ty.is_str() { "str" - } else if is_type_diagnostic_item(cx, ty, sym::String) { + } else if is_type_lang_item(cx, ty, LangItem::String) { "String" } else { return; diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index b3c2c7c9a2dcc..d226c0bba6593 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::{source_map::Spanned, symbol::sym, Span}; +use rustc_span::{source_map::Spanned, Span}; use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS; @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>( if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()); let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String); + if recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String); then { span_lint_and_help( cx, diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index d0cf411dfd34c..a9189b31c5710 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( if (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref) && { let arg_type = cx.typeck_results().expr_ty(receiver); let base_type = arg_type.peel_refs(); - *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::String) + *base_type.kind() == ty::Str || is_type_lang_item(cx, base_type, hir::LangItem::String) } { receiver } else { @@ -50,7 +50,7 @@ pub(super) fn check<'tcx>( // converted to string. fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { let arg_ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, arg_ty, sym::String) { + if is_type_lang_item(cx, arg_ty, hir::LangItem::String) { return false; } if let ty::Ref(_, ty, ..) = arg_ty.kind() { diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index ede3b8bb74e97..4f4f543e8a912 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth}; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{Symbol, sym}; use super::INEFFICIENT_TO_STRING; @@ -60,7 +60,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { return true; } - if is_type_diagnostic_item(cx, ty, sym::String) { + if is_type_lang_item(cx, ty, hir::LangItem::String) { return true; } diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 8b6b8f1bf16cb..13c47c03a80dd 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -36,14 +36,14 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { } } else { let ty = cx.typeck_results().expr_ty(e); - if is_type_diagnostic_item(cx, ty, sym::String) + if is_type_lang_item(cx, ty, LangItem::String) || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str)) || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str)) { Some(RepeatKind::String) } else { let ty = ty.peel_refs(); - (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)).then_some(RepeatKind::String) + (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)).then_some(RepeatKind::String) } } } @@ -58,7 +58,7 @@ pub(super) fn check( if_chain! { if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String); + if is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String); 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); diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs index a76341855b6db..01655e860c43f 100644 --- a/clippy_lints/src/methods/no_effect_replace.rs +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::SpanlessEq; use if_chain::if_chain; use rustc_ast::LitKind; -use rustc_hir::ExprKind; +use rustc_hir::{ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::sym; use super::NO_EFFECT_REPLACE; @@ -16,7 +15,7 @@ pub(super) fn check<'tcx>( arg2: &'tcx rustc_hir::Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - if !(ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)) { + if !(ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) { return; } diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index 0a14f9216ab38..a345ec813ff50 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -1,11 +1,10 @@ use clippy_utils::consts::{constant_context, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; -use rustc_hir::Expr; +use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; -use rustc_span::sym; use super::REPEAT_ONCE; @@ -37,7 +36,7 @@ pub(super) fn check<'tcx>( format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)), Applicability::MachineApplicable, ); - } else if is_type_diagnostic_item(cx, ty, sym::String) { + } else if is_type_lang_item(cx, ty, LangItem::String) { span_lint_and_sugg( cx, REPEAT_ONCE, diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 324c9c17b5a9a..1c031ad6acbaf 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_trait_method, strip_pat_refs}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -105,7 +105,7 @@ pub(super) fn check<'tcx>( 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) { + if is_type_lang_item(cx, self_ty, hir::LangItem::String) { true } else { *self_ty.kind() == ty::Str diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 6974260f70dbd..6f4cec546e969 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -1,18 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::method_chain_args; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::sym; use super::STRING_EXTEND_CHARS; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if !is_type_diagnostic_item(cx, obj_ty, sym::String) { + if !is_type_lang_item(cx, obj_ty, hir::LangItem::String) { return; } if let Some(arglists) = method_chain_args(arg, &["chars"]) { @@ -20,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let ref_str = if *self_ty.kind() == ty::Str { "" - } else if is_type_diagnostic_item(cx, self_ty, sym::String) { + } else if is_type_lang_item(cx, self_ty, hir::LangItem::String) { "&" } else { return; diff --git a/clippy_lints/src/methods/unnecessary_join.rs b/clippy_lints/src/methods/unnecessary_join.rs index 973b8a7e6bf6a..c9b87bc6bf29d 100644 --- a/clippy_lints/src/methods/unnecessary_join.rs +++ b/clippy_lints/src/methods/unnecessary_join.rs @@ -1,10 +1,10 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item}; +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; use rustc_middle::ty::{Ref, Slice}; -use rustc_span::{sym, Span}; +use rustc_span::Span; use super::UNNECESSARY_JOIN; @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( // the turbofish for collect is ::> if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind(); if let Slice(slice) = ref_type.kind(); - if is_type_diagnostic_item(cx, *slice, sym::String); + if is_type_lang_item(cx, *slice, LangItem::String); // the argument for join is "" if let ExprKind::Lit(spanned) = &join_arg.kind; if let LitKind::Str(symbol, _) = spanned.node; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index b2e9ce5c94d65..79aa15b06ef4d 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::ptr::get_spans; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::{get_trait_def_id, is_self, paths}; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -11,7 +11,7 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{ BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind, }; -use rustc_hir::{HirIdMap, HirIdSet}; +use rustc_hir::{HirIdMap, HirIdSet, LangItem}; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; @@ -249,7 +249,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } } - if is_type_diagnostic_item(cx, ty, sym::String) { + if is_type_lang_item(cx, ty, LangItem::String) { if let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) { diag.span_suggestion( diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 0d74c90a834f7..612ee8a55a66a 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -450,7 +450,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( substs.type_at(0), ), ), - Some(sym::String) => ( + _ if Some(adt.did()) == cx.tcx.lang_items().string() => ( [("clone", ".to_owned()"), ("as_str", "")].as_slice(), DerefTy::Str, ), diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index aedbe08e3e46e..c1677fb3da1c4 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth}; use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{def_id, Body, FnDecl, HirId}; +use rustc_hir::{def_id, Body, FnDecl, HirId, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir; use rustc_middle::ty::{self, Ty}; @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD) || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD) || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD) - && is_type_diagnostic_item(cx, arg_ty, sym::String)); + && is_type_lang_item(cx, arg_ty, LangItem::String)); let from_deref = !from_borrow && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF) diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index d356c99c8fc47..f4705481d4e69 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths}; use clippy_utils::{peel_blocks, SpanlessEq}; use if_chain::if_chain; @@ -190,7 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { }, ExprKind::Index(target, _idx) => { let e_ty = cx.typeck_results().expr_ty(target).peel_refs(); - if matches!(e_ty.kind(), ty::Str) || is_type_diagnostic_item(cx, e_ty, sym::String) { + if matches!(e_ty.kind(), ty::Str) || is_type_lang_item(cx, e_ty, LangItem::String) { span_lint( cx, STRING_SLICE, @@ -205,7 +205,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String) + is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) } fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { @@ -446,7 +446,7 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind; if path.ident.name == sym::to_string; let ty = cx.typeck_results().expr_ty(self_arg); - if is_type_diagnostic_item(cx, ty, sym::String); + if is_type_lang_item(cx, ty, LangItem::String); then { span_lint_and_help( cx, diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index 08020ce663817..802415e163df5 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -37,18 +37,19 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let param = qpath_generic_tys(qpath).next()?; let id = path_def_id(cx, param)?; - cx.tcx.get_diagnostic_name(id).filter(|&name| { - matches!( - name, - sym::HashMap - | sym::String - | sym::Vec - | sym::HashSet - | sym::VecDeque - | sym::LinkedList - | sym::BTreeMap - | sym::BTreeSet - | sym::BinaryHeap - ) - }) + cx.tcx + .get_diagnostic_name(id) + .filter(|&name| matches!(name, sym::HashMap | sym::Vec | sym::HashSet + | sym::VecDeque + | sym::LinkedList + | sym::BTreeMap + | sym::BTreeSet + | sym::BinaryHeap)) + .or_else(|| { + cx.tcx + .lang_items() + .string() + .filter(|did| id == *did) + .map(|_| sym::String) + }) } diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index fa567b9b2d243..855137b14d84b 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -91,10 +91,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { let ty = qpath_generic_tys(qpath).next()?; let id = path_def_id(cx, ty)?; - let path = match cx.tcx.get_diagnostic_name(id)? { - sym::String => "str", - sym::OsString => "std::ffi::OsStr", - sym::PathBuf => "std::path::Path", + let path = match cx.tcx.get_diagnostic_name(id) { + Some(sym::OsString) => "std::ffi::OsStr", + Some(sym::PathBuf) => "std::path::Path", + _ if Some(id) == cx.tcx.lang_items().string() => "str", _ => return None, }; Some(path) diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index ab73f0fc44f4e..9f207d32fcfff 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -1,13 +1,12 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item}; +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item}; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -61,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { if let LitKind::Str(symbol, _) = spanned.node; if symbol.is_empty(); let inner_expr_type = cx.typeck_results().expr_ty(inner_expr); - if is_type_diagnostic_item(cx, inner_expr_type, sym::String); + if is_type_lang_item(cx, inner_expr_type, LangItem::String); then { span_lint_and_sugg( cx, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d32cf1a793672..fac91dfdbda01 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -434,6 +434,16 @@ pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[ path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments)) } +/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if +/// it matches the given lang item. +pub fn is_path_lang_item<'tcx>( + cx: &LateContext<'_>, + maybe_path: &impl MaybePath<'tcx>, + lang_item: LangItem, +) -> bool { + path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id)) +} + /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if /// it matches the given diagnostic item. pub fn is_path_diagnostic_item<'tcx>( @@ -760,7 +770,6 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - /// constructor from the std library fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool { let std_types_symbols = &[ - sym::String, sym::Vec, sym::VecDeque, sym::LinkedList, @@ -777,7 +786,7 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() { return std_types_symbols .iter() - .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did())); + .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()); } } } @@ -834,7 +843,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: & ExprKind::Lit(hir::Lit { node: LitKind::Str(ref sym, _), .. - }) => return sym.is_empty() && is_path_diagnostic_item(cx, ty, sym::String), + }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String), ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), ExprKind::Repeat(_, ArrayLen::Body(len)) => { if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind && From 921f4d317ec3498b0583a72ba1dfdfd84699c268 Mon Sep 17 00:00:00 2001 From: koka Date: Fri, 18 Nov 2022 21:23:16 +0900 Subject: [PATCH 153/169] Keep original literal notation in suggestion --- clippy_lints/src/unused_rounding.rs | 2 +- tests/ui/unused_rounding.fixed | 5 +++++ tests/ui/unused_rounding.rs | 5 +++++ tests/ui/unused_rounding.stderr | 8 +++++++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 3164937293b68..0f6f36da82088 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -36,7 +36,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { && let ExprKind::Lit(spanned) = &receiver.kind && let LitKind::Float(symbol, ty) = spanned.kind { let f = symbol.as_str().parse::().unwrap(); - let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { + let f_str = spanned.token_lit.symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { ty.name_str() } else { "" diff --git a/tests/ui/unused_rounding.fixed b/tests/ui/unused_rounding.fixed index 54f85806ac3b3..38fe6c34cfec1 100644 --- a/tests/ui/unused_rounding.fixed +++ b/tests/ui/unused_rounding.fixed @@ -6,4 +6,9 @@ fn main() { let _ = 1.0f64; let _ = 1.00f32; let _ = 2e-54f64.floor(); + + // issue9866 + let _ = 3.3_f32.round(); + let _ = 3.3_f64.round(); + let _ = 3.0_f32; } diff --git a/tests/ui/unused_rounding.rs b/tests/ui/unused_rounding.rs index 8d007bc4a1dc8..a5cac64d023ae 100644 --- a/tests/ui/unused_rounding.rs +++ b/tests/ui/unused_rounding.rs @@ -6,4 +6,9 @@ fn main() { let _ = 1.0f64.floor(); let _ = 1.00f32.round(); let _ = 2e-54f64.floor(); + + // issue9866 + let _ = 3.3_f32.round(); + let _ = 3.3_f64.round(); + let _ = 3.0_f32.round(); } diff --git a/tests/ui/unused_rounding.stderr b/tests/ui/unused_rounding.stderr index 6cfb02e040283..1eeb5d1de8832 100644 --- a/tests/ui/unused_rounding.stderr +++ b/tests/ui/unused_rounding.stderr @@ -18,5 +18,11 @@ error: used the `round` method with a whole number float LL | let _ = 1.00f32.round(); | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `1.00f32` -error: aborting due to 3 previous errors +error: used the `round` method with a whole number float + --> $DIR/unused_rounding.rs:13:13 + | +LL | let _ = 3.0_f32.round(); + | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `3.0_f32` + +error: aborting due to 4 previous errors From 928a158716b8aaee0cf1c40973b027cd63710c99 Mon Sep 17 00:00:00 2001 From: koka Date: Fri, 18 Nov 2022 21:51:43 +0900 Subject: [PATCH 154/169] Allow manual swap in const fn --- clippy_lints/src/swap.rs | 6 +++++- tests/ui/swap.fixed | 9 +++++++++ tests/ui/swap.rs | 9 +++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index f46c21e126552..68a601705516b 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_mut_borrow_both, eq_expr_value, std_or_core}; +use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; @@ -138,6 +138,10 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa /// Implementation of the `MANUAL_SWAP` lint. fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { + if in_constant(cx, block.hir_id) { + return; + } + for w in block.stmts.windows(3) { if_chain! { // let t = foo(); diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed index 24b229235d33a..805a2ba5a5984 100644 --- a/tests/ui/swap.fixed +++ b/tests/ui/swap.fixed @@ -155,3 +155,12 @@ fn issue_8154() { let s = S3(&mut s); std::mem::swap(&mut s.0.x, &mut s.0.y); } + +const fn issue_9864(mut u: u32) -> u32 { + let mut v = 10; + + let temp = u; + u = v; + v = temp; + u + v +} diff --git a/tests/ui/swap.rs b/tests/ui/swap.rs index a318c27919c8a..a8c878479523f 100644 --- a/tests/ui/swap.rs +++ b/tests/ui/swap.rs @@ -179,3 +179,12 @@ fn issue_8154() { s.0.x = s.0.y; s.0.y = t; } + +const fn issue_9864(mut u: u32) -> u32 { + let mut v = 10; + + let temp = u; + u = v; + v = temp; + u + v +} From 3c86cade4ea11baebaadb96402f8987fa013b31c Mon Sep 17 00:00:00 2001 From: koka Date: Sat, 19 Nov 2022 00:28:02 +0900 Subject: [PATCH 155/169] Note about const fn Since `std::mem::swap` is not stable as a const fn, the suggestion would not be applicable in that cases --- clippy_lints/src/swap.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 68a601705516b..c374529d1ea9f 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -16,6 +16,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for manual swapping. /// + /// Note that the lint will not be emitted in const blocks, as the suggestion would not be applicable. + /// /// ### Why is this bad? /// The `std::mem::swap` function exposes the intent better /// without deinitializing or copying either variable. From 3a2eaa73f414105cbd8897381e4a3db672d98845 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 19 Nov 2022 02:22:24 +0000 Subject: [PATCH 156/169] drive-by: Add is_async fn to hir::IsAsync --- clippy_lints/src/manual_async_fn.rs | 4 ++-- clippy_lints/src/unused_async.rs | 4 ++-- clippy_utils/src/lib.rs | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 090f9f8ff73cf..5c6a342b3d074 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, - HirId, IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind, + HirId, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { ) { if_chain! { if let Some(header) = kind.header(); - if header.asyncness == IsAsync::NotAsync; + if !header.asyncness.is_async(); // Check that this function returns `impl Future` if let FnRetTy::Return(ret_ty) = decl.output; if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty); diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index bf487c7ca20c8..3538bef6e0618 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, IsAsync, YieldSource}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { span: Span, hir_id: HirId, ) { - if !span.from_expansion() && fn_kind.asyncness() == IsAsync::Async { + if !span.from_expansion() && fn_kind.asyncness().is_async() { let mut visitor = AsyncFnVisitor { cx, found_await: false }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), hir_id); if !visitor.found_await { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d32cf1a793672..bb91317d67f5a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -87,10 +87,10 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, Expr, - ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, - Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, - TraitRef, TyKind, UnOp, + def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, + Destination, Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, + LangItem, Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, + QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -1861,7 +1861,7 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, /// Checks if the given function kind is an async function. pub fn is_async_fn(kind: FnKind<'_>) -> bool { - matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async) + matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness.is_async()) } /// Peels away all the compiler generated code surrounding the body of an async function, From ef5f60285faa6444e293214bd0137511f4e40d0d Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 18 Nov 2022 18:18:58 +0000 Subject: [PATCH 157/169] Move `line_span` to source.rs --- clippy_utils/src/lib.rs | 20 +------------------- clippy_utils/src/source.rs | 22 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 670b740d8418c..b0063758dcef0 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -108,11 +108,10 @@ use rustc_middle::ty::{FloatTy, IntTy, UintTy}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; -use rustc_span::source_map::original_sp; use rustc_span::source_map::SourceMap; use rustc_span::sym; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; @@ -1302,23 +1301,6 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { .is_some() } -/// Extends the span to the beginning of the spans line, incl. whitespaces. -/// -/// ```rust -/// let x = (); -/// // ^^ -/// // will be converted to -/// let x = (); -/// // ^^^^^^^^^^^^^^ -/// ``` -fn line_span(cx: &T, span: Span) -> Span { - let span = original_sp(span, DUMMY_SP); - let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); - let line_no = source_map_and_line.line; - let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]); - span.with_lo(line_start) -} - /// 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/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index d28bd92d708ba..eacfa91ba556d 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -2,13 +2,12 @@ #![allow(clippy::module_name_repetitions)] -use crate::line_span; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::hygiene; -use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext}; +use rustc_span::source_map::{original_sp, SourceMap}; +use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP}; use std::borrow::Cow; /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. @@ -55,6 +54,23 @@ fn first_char_in_first_line(cx: &T, span: Span) -> Option(cx: &T, span: Span) -> Span { + let span = original_sp(span, DUMMY_SP); + let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); + let line_no = source_map_and_line.line; + let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]); + span.with_lo(line_start) +} + /// Returns the indentation of the line of a span /// /// ```rust,ignore From 98b343c5e6a5059a420d0b2d38edad0b719520e8 Mon Sep 17 00:00:00 2001 From: Caio Date: Sat, 19 Nov 2022 08:22:27 -0300 Subject: [PATCH 158/169] [arithmetic-side-effects] Detect overflowing associated constants of integers --- .../src/operators/arithmetic_side_effects.rs | 115 +++++++++--------- .../arithmetic_side_effects_allowed.rs | 11 +- tests/ui/arithmetic_side_effects.rs | 8 +- tests/ui/arithmetic_side_effects.stderr | 110 ++++++++--------- 4 files changed, 130 insertions(+), 114 deletions(-) diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 8827daaa3ee7c..eda65e3015357 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,12 +1,19 @@ use super::ARITHMETIC_SIDE_EFFECTS; -use clippy_utils::{consts::constant_simple, diagnostics::span_lint}; +use clippy_utils::{ + consts::{constant, constant_simple}, + diagnostics::span_lint, + peel_hir_expr_refs, +}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; -use rustc_span::source_map::{Span, Spanned}; +use rustc_span::{ + source_map::{Span, Spanned}, + sym, +}; const HARD_CODED_ALLOWED: &[&str] = &[ "&str", @@ -38,24 +45,6 @@ impl ArithmeticSideEffects { } } - /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a - /// non-constant environment that won't overflow. - fn has_valid_op(op: &Spanned, expr: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::Lit(ref lit) = expr.kind && - let ast::LitKind::Int(value, _) = lit.node - { - match (&op.node, value) { - (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, - (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) - | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) - | (hir::BinOpKind::Mul, 0 | 1) => true, - _ => false, - } - } else { - false - } - } - /// Checks if the given `expr` has any of the inner `allowed` elements. fn is_allowed_ty(&self, ty: Ty<'_>) -> bool { self.allowed @@ -74,15 +63,14 @@ impl ArithmeticSideEffects { self.expr_span = Some(expr.span); } - /// If `expr` does not match any variant of `LiteralIntegerTy`, returns `None`. - fn literal_integer<'expr, 'tcx>(expr: &'expr hir::Expr<'tcx>) -> Option> { - if matches!(expr.kind, hir::ExprKind::Lit(_)) { - return Some(LiteralIntegerTy::Value(expr)); + /// If `expr` is not a literal integer like `1`, returns `None`. + fn literal_integer(expr: &hir::Expr<'_>) -> Option { + if let hir::ExprKind::Lit(ref lit) = expr.kind && let ast::LitKind::Int(n, _) = lit.node { + Some(n) } - if let hir::ExprKind::AddrOf(.., inn) = expr.kind && let hir::ExprKind::Lit(_) = inn.kind { - return Some(LiteralIntegerTy::Ref(inn)); + else { + None } - None } /// Manages when the lint should be triggered. Operations in constant environments, hard coded @@ -117,10 +105,20 @@ impl ArithmeticSideEffects { return; } let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { - match (Self::literal_integer(lhs), Self::literal_integer(rhs)) { - (None, Some(lit_int_ty)) | (Some(lit_int_ty), None) => Self::has_valid_op(op, lit_int_ty.into()), - (Some(LiteralIntegerTy::Value(_)), Some(LiteralIntegerTy::Value(_))) => true, - (None, None) | (Some(_), Some(_)) => false, + let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); + let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); + match (Self::literal_integer(actual_lhs), Self::literal_integer(actual_rhs)) { + (None, None) => false, + (None, Some(n)) | (Some(n), None) => match (&op.node, n) { + (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, + (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) + | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) + | (hir::BinOpKind::Mul, 0 | 1) => true, + _ => false, + }, + (Some(_), Some(_)) => { + matches!((lhs_ref_counter, rhs_ref_counter), (0, 0)) + }, } } else { false @@ -129,21 +127,45 @@ impl ArithmeticSideEffects { self.issue_lint(cx, expr); } } + + fn manage_unary_ops<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + expr: &hir::Expr<'tcx>, + un_expr: &hir::Expr<'tcx>, + un_op: hir::UnOp, + ) { + let hir::UnOp::Neg = un_op else { return; }; + if constant(cx, cx.typeck_results(), un_expr).is_some() { + return; + } + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + if self.is_allowed_ty(ty) { + return; + } + let actual_un_expr = peel_hir_expr_refs(un_expr).0; + if Self::literal_integer(actual_un_expr).is_some() { + return; + } + self.issue_lint(cx, expr); + } + + fn should_skip_expr(&mut self, expr: &hir::Expr<'_>) -> bool { + self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) + } } impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) { - if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) { + if self.should_skip_expr(expr) { return; } match &expr.kind { - hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => { + hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => { self.manage_bin_ops(cx, expr, op, lhs, rhs); }, - hir::ExprKind::Unary(hir::UnOp::Neg, _) => { - if constant_simple(cx, cx.typeck_results(), expr).is_none() { - self.issue_lint(cx, expr); - } + hir::ExprKind::Unary(un_op, un_expr) => { + self.manage_unary_ops(cx, expr, un_expr, *un_op); }, _ => {}, } @@ -177,22 +199,3 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { } } } - -/// Tells if an expression is a integer declared by value or by reference. -/// -/// If `LiteralIntegerTy::Ref`, then the contained value will be `hir::ExprKind::Lit` rather -/// than `hirExprKind::Addr`. -enum LiteralIntegerTy<'expr, 'tcx> { - /// For example, `&199` - Ref(&'expr hir::Expr<'tcx>), - /// For example, `1` or `i32::MAX` - Value(&'expr hir::Expr<'tcx>), -} - -impl<'expr, 'tcx> From> for &'expr hir::Expr<'tcx> { - fn from(from: LiteralIntegerTy<'expr, 'tcx>) -> Self { - match from { - LiteralIntegerTy::Ref(elem) | LiteralIntegerTy::Value(elem) => elem, - } - } -} diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs index 1aed09b7c7bd4..e8a023ab17643 100644 --- a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs +++ b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs @@ -1,6 +1,6 @@ #![warn(clippy::arithmetic_side_effects)] -use core::ops::Add; +use core::ops::{Add, Neg}; #[derive(Clone, Copy)] struct Point { @@ -16,9 +16,18 @@ impl Add for Point { } } +impl Neg for Point { + type Output = Self; + + fn neg(self) -> Self::Output { + todo!() + } +} + fn main() { let _ = Point { x: 1, y: 0 } + Point { x: 2, y: 3 }; let point: Point = Point { x: 1, y: 0 }; let _ = point + point; + let _ = -point; } diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index b25e68f13061b..b5ed8988a518f 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -150,8 +150,12 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n = 23 + 85; // Unary - _n = -1; - _n = -(-1); + _n = -2147483647; + _n = -i32::MAX; + _n = -i32::MIN; + _n = -&2147483647; + _n = -&i32::MAX; + _n = -&i32::MIN; } pub fn runtime_ops() { diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index 0f06e22bae96b..0259a0824e794 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -19,331 +19,331 @@ LL | let _ = inferred_string + ""; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:161:5 + --> $DIR/arithmetic_side_effects.rs:165:5 | LL | _n += 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:162:5 + --> $DIR/arithmetic_side_effects.rs:166:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:163:5 + --> $DIR/arithmetic_side_effects.rs:167:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:164:5 + --> $DIR/arithmetic_side_effects.rs:168:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:165:5 + --> $DIR/arithmetic_side_effects.rs:169:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:166:5 + --> $DIR/arithmetic_side_effects.rs:170:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:167:5 + --> $DIR/arithmetic_side_effects.rs:171:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:168:5 + --> $DIR/arithmetic_side_effects.rs:172:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:169:5 + --> $DIR/arithmetic_side_effects.rs:173:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:170:5 + --> $DIR/arithmetic_side_effects.rs:174:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:173:10 + --> $DIR/arithmetic_side_effects.rs:177:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:174:10 + --> $DIR/arithmetic_side_effects.rs:178:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:175:10 + --> $DIR/arithmetic_side_effects.rs:179:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:176:10 + --> $DIR/arithmetic_side_effects.rs:180:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:177:10 + --> $DIR/arithmetic_side_effects.rs:181:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:178:10 + --> $DIR/arithmetic_side_effects.rs:182:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:179:10 + --> $DIR/arithmetic_side_effects.rs:183:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:180:10 + --> $DIR/arithmetic_side_effects.rs:184:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:181:10 + --> $DIR/arithmetic_side_effects.rs:185:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:182:10 + --> $DIR/arithmetic_side_effects.rs:186:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:183:10 + --> $DIR/arithmetic_side_effects.rs:187:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:184:10 + --> $DIR/arithmetic_side_effects.rs:188:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:185:10 + --> $DIR/arithmetic_side_effects.rs:189:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:186:10 + --> $DIR/arithmetic_side_effects.rs:190:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:187:10 + --> $DIR/arithmetic_side_effects.rs:191:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:188:10 + --> $DIR/arithmetic_side_effects.rs:192:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:189:10 + --> $DIR/arithmetic_side_effects.rs:193:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:190:10 + --> $DIR/arithmetic_side_effects.rs:194:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:191:10 + --> $DIR/arithmetic_side_effects.rs:195:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:194:13 + --> $DIR/arithmetic_side_effects.rs:198:13 | LL | let _ = Custom + 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:195:13 + --> $DIR/arithmetic_side_effects.rs:199:13 | LL | let _ = Custom + 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:196:13 + --> $DIR/arithmetic_side_effects.rs:200:13 | LL | let _ = Custom + 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:197:13 + --> $DIR/arithmetic_side_effects.rs:201:13 | LL | let _ = Custom + 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:198:13 + --> $DIR/arithmetic_side_effects.rs:202:13 | LL | let _ = Custom + 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:199:13 + --> $DIR/arithmetic_side_effects.rs:203:13 | LL | let _ = Custom + 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:200:13 + --> $DIR/arithmetic_side_effects.rs:204:13 | LL | let _ = Custom - 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:201:13 + --> $DIR/arithmetic_side_effects.rs:205:13 | LL | let _ = Custom - 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:202:13 + --> $DIR/arithmetic_side_effects.rs:206:13 | LL | let _ = Custom - 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:203:13 + --> $DIR/arithmetic_side_effects.rs:207:13 | LL | let _ = Custom - 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:204:13 + --> $DIR/arithmetic_side_effects.rs:208:13 | LL | let _ = Custom - 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:205:13 + --> $DIR/arithmetic_side_effects.rs:209:13 | LL | let _ = Custom - 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:206:13 + --> $DIR/arithmetic_side_effects.rs:210:13 | LL | let _ = Custom / 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:207:13 + --> $DIR/arithmetic_side_effects.rs:211:13 | LL | let _ = Custom / 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:208:13 + --> $DIR/arithmetic_side_effects.rs:212:13 | LL | let _ = Custom / 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:209:13 + --> $DIR/arithmetic_side_effects.rs:213:13 | LL | let _ = Custom / 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:210:13 + --> $DIR/arithmetic_side_effects.rs:214:13 | LL | let _ = Custom / 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:211:13 + --> $DIR/arithmetic_side_effects.rs:215:13 | LL | let _ = Custom / 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:212:13 + --> $DIR/arithmetic_side_effects.rs:216:13 | LL | let _ = Custom * 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:213:13 + --> $DIR/arithmetic_side_effects.rs:217:13 | LL | let _ = Custom * 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:214:13 + --> $DIR/arithmetic_side_effects.rs:218:13 | LL | let _ = Custom * 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:215:13 + --> $DIR/arithmetic_side_effects.rs:219:13 | LL | let _ = Custom * 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:216:13 + --> $DIR/arithmetic_side_effects.rs:220:13 | LL | let _ = Custom * 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:217:13 + --> $DIR/arithmetic_side_effects.rs:221:13 | LL | let _ = Custom * 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:220:10 + --> $DIR/arithmetic_side_effects.rs:224:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:221:10 + --> $DIR/arithmetic_side_effects.rs:225:10 | LL | _n = -&_n; | ^^^^ From e0b1463e0a0133ae49fc31aab3e7f18882ff00b0 Mon Sep 17 00:00:00 2001 From: Caio Date: Sat, 19 Nov 2022 08:25:20 -0300 Subject: [PATCH 159/169] Remove unused --- clippy_lints/src/operators/arithmetic_side_effects.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index eda65e3015357..20b82d81a2aeb 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -10,10 +10,7 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; -use rustc_span::{ - source_map::{Span, Spanned}, - sym, -}; +use rustc_span::source_map::{Span, Spanned}; const HARD_CODED_ALLOWED: &[&str] = &[ "&str", From 1baa6cd591982471475895fc3256d5162a651628 Mon Sep 17 00:00:00 2001 From: koka Date: Sat, 19 Nov 2022 21:21:47 +0900 Subject: [PATCH 160/169] refac: grab a snip from receiver --- clippy_lints/src/unused_rounding.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 0f6f36da82088..cf34a57f91d12 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind}; +use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet}; +use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -29,19 +29,15 @@ declare_clippy_lint! { } declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); -fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { +fn is_useless_rounding<'a>(cx: &EarlyContext<'a>, expr: &'a Expr) -> Option<(&'a str, String)> { if let ExprKind::MethodCall(name_ident, receiver, _, _) = &expr.kind && let method_name = name_ident.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") && let ExprKind::Lit(spanned) = &receiver.kind - && let LitKind::Float(symbol, ty) = spanned.kind { + && let LitKind::Float(symbol, _) = spanned.kind { let f = symbol.as_str().parse::().unwrap(); - let f_str = spanned.token_lit.symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { - ty.name_str() - } else { - "" - }; if f.fract() == 0.0 { + let f_str = snippet(cx, receiver.span, "..").to_string(); Some((method_name, f_str)) } else { None @@ -53,7 +49,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { impl EarlyLintPass for UnusedRounding { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if let Some((method_name, float)) = is_useless_rounding(expr) { + if let Some((method_name, float)) = is_useless_rounding(cx, expr) { span_lint_and_sugg( cx, UNUSED_ROUNDING, From 4d8af99365d73725b4d46bfc4cd993d6c3af63c5 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 19 Nov 2022 18:13:17 +0000 Subject: [PATCH 161/169] Fix `#[allow]` for module_name_repetitions & single_component_path_imports --- clippy_lints/src/attrs.rs | 4 +- clippy_lints/src/lib.rs | 2 +- .../src/single_component_path_imports.rs | 219 ++++++++++-------- tests/ui/single_component_path_imports.stderr | 12 +- ...component_path_imports_nested_first.stderr | 15 +- tests/ui/useless_attribute.fixed | 21 +- tests/ui/useless_attribute.rs | 21 +- tests/ui/useless_attribute.stderr | 6 +- 8 files changed, 180 insertions(+), 120 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 235ab4977ce4b..ecf8e83375dbf 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -378,7 +378,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { | "enum_glob_use" | "redundant_pub_crate" | "macro_use_imports" - | "unsafe_removed_from_name", + | "unsafe_removed_from_name" + | "module_name_repetitions" + | "single_component_path_imports" ) }) { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4e640d7ebf651..3ab5031696d53 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -792,7 +792,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); store.register_early_pass(|| Box::new(as_conversions::AsConversions)); store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore)); - store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports)); + store.register_early_pass(|| Box::::default()); let max_fn_params_bools = conf.max_fn_params_bools; let max_struct_bools = conf.max_struct_bools; store.register_late_pass(move |_| { diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 66b79513032f6..2036e85db7e8c 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use rustc_ast::node_id::{NodeId, NodeMap}; use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{edition::Edition, symbol::kw, Span, Symbol}; declare_clippy_lint! { @@ -33,51 +34,32 @@ declare_clippy_lint! { "imports with single component path are redundant" } -declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); +#[derive(Default)] +pub struct SingleComponentPathImports { + /// Buffer found usages to emit when visiting that item so that `#[allow]` works as expected + found: NodeMap>, +} + +struct SingleUse { + name: Symbol, + span: Span, + item_id: NodeId, + can_suggest: bool, +} + +impl_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; } - check_mod(cx, &krate.items); - } -} -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(); - - // 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, - &mut macros, - ); + self.check_mod(cx, &krate.items); } - for (name, span, can_suggest) in single_use_usages { - if !imports_reused_with_self.contains(&name) { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + for SingleUse { span, can_suggest, .. } in self.found.remove(&item.id).into_iter().flatten() { if can_suggest { span_lint_and_sugg( cx, @@ -102,74 +84,127 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { } } -fn track_uses( - cx: &EarlyContext<'_>, - item: &Item, - imports_reused_with_self: &mut Vec, - single_use_usages: &mut Vec<(Symbol, Span, bool)>, - macros: &mut Vec, -) { - if item.span.from_expansion() || item.vis.kind.is_pub() { - return; - } +impl SingleComponentPathImports { + fn check_mod(&mut self, cx: &EarlyContext<'_>, items: &[P]) { + // keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example + // below. Removing the `use crypto_hash;` would make this a compile error + // ``` + // use crypto_hash; + // + // use self::crypto_hash::{Algorithm, Hasher}; + // ``` + let mut imports_reused_with_self = Vec::new(); - match &item.kind { - 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; - - // keep track of `use some_module;` usages - if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = use_tree.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push((name, item.span, true)); - } - } - return; + // keep track of single use statements such as `crypto_hash` in the example below + // ``` + // use crypto_hash; + // ``` + 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) + // ``` + // macro_rules! foo { () => {} }; + // pub(crate) use foo; + // ``` + let mut macros = Vec::new(); + + for item in items { + self.track_uses( + cx, + item, + &mut imports_reused_with_self, + &mut single_use_usages, + &mut macros, + ); + } + + for usage in single_use_usages { + if !imports_reused_with_self.contains(&usage.name) { + self.found.entry(usage.item_id).or_default().push(usage); } + } + } - 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.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = tree.0.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push((name, tree.0.span, false)); - } - } + fn track_uses( + &mut self, + cx: &EarlyContext<'_>, + item: &Item, + imports_reused_with_self: &mut Vec, + single_use_usages: &mut Vec, + macros: &mut Vec, + ) { + if item.span.from_expansion() || item.vis.kind.is_pub() { + return; + } + + match &item.kind { + ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { + self.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; + + // keep track of `use some_module;` usages + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = use_tree.kind { + let name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: item.span, + item_id: item.id, + can_suggest: true, + }); } } + return; } - } 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 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 name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: tree.0.span, + item_id: item.id, + can_suggest: 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.stderr b/tests/ui/single_component_path_imports.stderr index 509c88ac256a8..71dcc25d6e5b8 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:23:5 + --> $DIR/single_component_path_imports.rs:5:1 | -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:5:1 + --> $DIR/single_component_path_imports.rs:23:5 | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely error: aborting due to 2 previous errors diff --git a/tests/ui/single_component_path_imports_nested_first.stderr b/tests/ui/single_component_path_imports_nested_first.stderr index 633546f6419a2..330f285202d0c 100644 --- a/tests/ui/single_component_path_imports_nested_first.stderr +++ b/tests/ui/single_component_path_imports_nested_first.stderr @@ -1,3 +1,11 @@ +error: this import is redundant + --> $DIR/single_component_path_imports_nested_first.rs:4:1 + | +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_nested_first.rs:13:10 | @@ -5,7 +13,6 @@ LL | use {regex, serde}; | ^^^^^ | = help: remove this import - = note: `-D clippy::single-component-path-imports` implied by `-D warnings` error: this import is redundant --> $DIR/single_component_path_imports_nested_first.rs:13:17 @@ -15,11 +22,5 @@ LL | use {regex, serde}; | = help: remove this import -error: this import is redundant - --> $DIR/single_component_path_imports_nested_first.rs:4:1 - | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely - error: aborting due to 3 previous errors diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index c23231a99e9f0..871e4fb5c3a9f 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![allow(unused)] #![warn(clippy::useless_attribute)] #![warn(unreachable_pub)] #![feature(rustc_private)] @@ -16,6 +17,13 @@ extern crate rustc_middle; #[macro_use] extern crate proc_macro_derive; +fn test_indented_attr() { + #![allow(clippy::almost_swapped)] + use std::collections::HashSet; + + let _ = HashSet::::default(); +} + // don't lint on unused_import for `use` items #[allow(unused_imports)] use std::collections; @@ -63,13 +71,16 @@ mod c { pub(crate) struct S; } -fn test_indented_attr() { - #![allow(clippy::almost_swapped)] - use std::collections::HashSet; - - let _ = HashSet::::default(); +// https://github.com/rust-lang/rust-clippy/issues/7511 +pub mod split { + #[allow(clippy::module_name_repetitions)] + pub use regex::SplitN; } +// https://github.com/rust-lang/rust-clippy/issues/8768 +#[allow(clippy::single_component_path_imports)] +use regex; + fn main() { test_indented_attr(); } diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 7a7b198ea6078..cb50736ba395a 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![allow(unused)] #![warn(clippy::useless_attribute)] #![warn(unreachable_pub)] #![feature(rustc_private)] @@ -16,6 +17,13 @@ extern crate rustc_middle; #[macro_use] extern crate proc_macro_derive; +fn test_indented_attr() { + #[allow(clippy::almost_swapped)] + use std::collections::HashSet; + + let _ = HashSet::::default(); +} + // don't lint on unused_import for `use` items #[allow(unused_imports)] use std::collections; @@ -63,13 +71,16 @@ mod c { pub(crate) struct S; } -fn test_indented_attr() { - #[allow(clippy::almost_swapped)] - use std::collections::HashSet; - - let _ = HashSet::::default(); +// https://github.com/rust-lang/rust-clippy/issues/7511 +pub mod split { + #[allow(clippy::module_name_repetitions)] + pub use regex::SplitN; } +// https://github.com/rust-lang/rust-clippy/issues/8768 +#[allow(clippy::single_component_path_imports)] +use regex; + fn main() { test_indented_attr(); } diff --git a/tests/ui/useless_attribute.stderr b/tests/ui/useless_attribute.stderr index 255d287635531..a7ea0df22945a 100644 --- a/tests/ui/useless_attribute.stderr +++ b/tests/ui/useless_attribute.stderr @@ -1,5 +1,5 @@ error: useless lint attribute - --> $DIR/useless_attribute.rs:8:1 + --> $DIR/useless_attribute.rs:9:1 | LL | #[allow(dead_code)] | ^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(dead_code)]` @@ -7,13 +7,13 @@ LL | #[allow(dead_code)] = note: `-D clippy::useless-attribute` implied by `-D warnings` error: useless lint attribute - --> $DIR/useless_attribute.rs:9:1 + --> $DIR/useless_attribute.rs:10:1 | 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:67:5 + --> $DIR/useless_attribute.rs:21:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` From 25e98bf708609d536ae53880c8f297917820f37e Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 21 Nov 2022 20:02:06 +0100 Subject: [PATCH 162/169] Bump Clippy version -> 0.1.67 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5223dca073fd7..6bdac84ada003 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.66" +version = "0.1.67" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 5368932b3528d..dcadd012a44d3 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.66" +version = "0.1.67" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 83fee7bb39c22..fb9f4740ecc50 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.66" +version = "0.1.67" edition = "2021" publish = false diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 68bb0be67a75e..578109840fb70 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.66" +version = "0.1.67" edition = "2021" publish = false From 661f13ce3b34dd2eb5bd0422ab464d41b725fc47 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 21 Nov 2022 20:02:14 +0100 Subject: [PATCH 163/169] Bump nightly version -> 2022-11-21 --- rust-toolchain | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust-toolchain b/rust-toolchain index 748d8a317160f..a806c1564796a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-10-20" -components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] +channel = "nightly-2022-11-21" +components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 5907e9155ed7f1312d108aa2110853472da3b029 Mon Sep 17 00:00:00 2001 From: ozkanonur Date: Sun, 20 Nov 2022 02:40:31 +0300 Subject: [PATCH 164/169] pass clippy sysroot env if given r=ozkanonur Signed-off-by: ozkanonur --- src/driver.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 51ae27fbbd7f6..ee2a3ad20d3e5 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -252,6 +252,13 @@ pub fn main() { exit(rustc_driver::catch_with_exit_code(move || { let mut orig_args: Vec = env::args().collect(); + let sys_root_env = std::env::var("SYSROOT").ok(); + let pass_sysroot_env_if_given = |args: &mut Vec, sys_root_env| { + if let Some(sys_root) = sys_root_env { + args.extend(vec!["--sysroot".into(), sys_root]); + }; + }; + // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc" // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver // uses @@ -259,7 +266,10 @@ pub fn main() { orig_args.remove(pos); orig_args[0] = "rustc".to_string(); - return rustc_driver::RunCompiler::new(&orig_args, &mut DefaultCallbacks).run(); + let mut args: Vec = orig_args.clone(); + pass_sysroot_env_if_given(&mut args, sys_root_env); + + return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run(); } if orig_args.iter().any(|a| a == "--version" || a == "-V") { @@ -282,6 +292,9 @@ pub fn main() { exit(0); } + let mut args: Vec = orig_args.clone(); + pass_sysroot_env_if_given(&mut args, sys_root_env); + let mut no_deps = false; let clippy_args_var = env::var("CLIPPY_ARGS").ok(); let clippy_args = clippy_args_var @@ -310,11 +323,10 @@ pub fn main() { let clippy_enabled = !cap_lints_allow && (!no_deps || in_primary_package); if clippy_enabled { - let mut args: Vec = orig_args.clone(); args.extend(clippy_args); rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run() } else { - rustc_driver::RunCompiler::new(&orig_args, &mut RustcCallbacks { clippy_args_var }).run() + rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run() } })) } From 05b914a92b9385066c756c9fc59822545e0fbe0a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 21 Nov 2022 20:15:50 +0100 Subject: [PATCH 165/169] Fix custom ICE message test on windows --- tests/ui-internal/custom_ice_message.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui-internal/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs index 5057a018300e9..4be04f77f5bdc 100644 --- a/tests/ui-internal/custom_ice_message.rs +++ b/tests/ui-internal/custom_ice_message.rs @@ -2,6 +2,7 @@ // 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" +// normalize-stderr-test: "'rustc'" -> "''" #![deny(clippy::internal)] #![allow(clippy::missing_clippy_version_attribute)] From d407778c7515eeb98c3513a1ecffb9dbc1e6b8b3 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 21 Nov 2022 20:52:06 +0100 Subject: [PATCH 166/169] Update Cargo.lock --- Cargo.lock | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb6140e74fea4..4f6367ef391eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -652,7 +652,7 @@ dependencies = [ [[package]] name = "clippy" -version = "0.1.66" +version = "0.1.67" dependencies = [ "clippy_lints", "clippy_utils", @@ -689,16 +689,16 @@ dependencies = [ "itertools", "opener", "shell-escape", - "tempfile", "walkdir", ] [[package]] name = "clippy_lints" -version = "0.1.66" +version = "0.1.67" dependencies = [ "cargo_metadata 0.14.0", "clippy_utils", + "declare_clippy_lint", "if_chain", "itertools", "pulldown-cmark", @@ -717,7 +717,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.66" +version = "0.1.67" dependencies = [ "arrayvec", "if_chain", @@ -1044,6 +1044,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" +[[package]] +name = "declare_clippy_lint" +version = "0.1.67" +dependencies = [ + "itertools", + "quote", + "syn", +] + [[package]] name = "derive-new" version = "0.5.8" @@ -2850,9 +2859,9 @@ checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" [[package]] name = "quote" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] From b4c90abb5f60cc5f25769e6ca5342b6f5c972e83 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 21 Nov 2022 20:52:12 +0100 Subject: [PATCH 167/169] Fix declare_clippy_lint crate --- src/tools/clippy/declare_clippy_lint/Cargo.toml | 3 +++ src/tools/clippy/declare_clippy_lint/src/lib.rs | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml index 578109840fb70..082570f1fe5d8 100644 --- a/src/tools/clippy/declare_clippy_lint/Cargo.toml +++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml @@ -11,3 +11,6 @@ proc-macro = true itertools = "0.10.1" quote = "1.0.21" syn = "1.0.100" + +[features] +deny-warnings = [] diff --git a/src/tools/clippy/declare_clippy_lint/src/lib.rs b/src/tools/clippy/declare_clippy_lint/src/lib.rs index 962766916dd1f..26210556d6526 100644 --- a/src/tools/clippy/declare_clippy_lint/src/lib.rs +++ b/src/tools/clippy/declare_clippy_lint/src/lib.rs @@ -1,5 +1,7 @@ #![feature(let_chains)] #![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)] use proc_macro::TokenStream; use quote::{format_ident, quote}; @@ -29,7 +31,7 @@ struct ClippyLint { } impl Parse for ClippyLint { - fn parse(input: ParseStream) -> Result { + fn parse(input: ParseStream<'_>) -> Result { let attrs = input.call(Attribute::parse_outer)?; let mut in_code = false; From 242280252d741f720f31b36309c9e2b37fc44fa7 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 21 Nov 2022 21:04:59 +0100 Subject: [PATCH 168/169] Clippy: Don't import GenericParamDefKind --- src/tools/clippy/clippy_utils/src/ty.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 897edfc5495f4..a26cdf6471645 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -13,9 +13,9 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericParamDefKind, IntTy, List, ParamEnv, - Predicate, PredicateKind, ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, + self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind, + ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, + VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; @@ -1011,7 +1011,7 @@ pub fn make_projection<'tcx>( assoc_item.def_id, substs.len(), generic_count, - params.map(GenericParamDefKind::descr).collect::>(), + params.map(ty::GenericParamDefKind::descr).collect::>(), substs, ); @@ -1022,9 +1022,9 @@ pub fn make_projection<'tcx>( .find(|(_, (param, arg))| { !matches!( (param, arg), - (GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) - | (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) - | (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) + (ty::GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) + | (ty::GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) + | (ty::GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) ) }) { @@ -1036,7 +1036,7 @@ pub fn make_projection<'tcx>( idx, param.descr(), arg, - params.map(GenericParamDefKind::descr).collect::>(), + params.map(ty::GenericParamDefKind::descr).collect::>(), substs, ); } From 5e6a9a40a90046a645e11a047ac2bd1d2dbbf456 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 22 Nov 2022 14:30:29 +0100 Subject: [PATCH 169/169] Clippy: Workaround for let_chains issue --- src/tools/clippy/src/driver.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index ee2a3ad20d3e5..ad6132a49baae 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -90,11 +90,12 @@ fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option) { // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever // it is rebuilt - if cfg!(debug_assertions) - && let Ok(current_exe) = env::current_exe() - && let Some(current_exe) = current_exe.to_str() - { - file_depinfo.insert(Symbol::intern(current_exe)); + if cfg!(debug_assertions) { + if let Ok(current_exe) = env::current_exe() + && let Some(current_exe) = current_exe.to_str() + { + file_depinfo.insert(Symbol::intern(current_exe)); + } } }