From 760c3fab4f0acff98ee32f05a0726c88e11fe814 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Fri, 26 Jul 2024 11:12:35 +0200 Subject: [PATCH 01/54] Fix case where `doc_markdown` is triggered on words ending with "ified" --- clippy_lints/src/doc/markdown.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clippy_lints/src/doc/markdown.rs b/clippy_lints/src/doc/markdown.rs index 41c0bcd55adca..237badb3f233c 100644 --- a/clippy_lints/src/doc/markdown.rs +++ b/clippy_lints/src/doc/markdown.rs @@ -92,6 +92,10 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b && matches!(prefix.chars().last(), Some('S' | 'X')) { prefix + } else if let Some(prefix) = s.strip_suffix("ified") + && prefix.chars().all(|c| c.is_ascii_uppercase()) + { + prefix } else { s.strip_suffix('s').unwrap_or(s) }; From 88506a9147172ff233f820451982e9514aee3476 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Fri, 26 Jul 2024 11:12:59 +0200 Subject: [PATCH 02/54] Add regression test for #13097 --- tests/ui/doc/doc_markdown-issue_13097.fixed | 13 +++++++++++++ tests/ui/doc/doc_markdown-issue_13097.rs | 13 +++++++++++++ tests/ui/doc/doc_markdown-issue_13097.stderr | 18 ++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 tests/ui/doc/doc_markdown-issue_13097.fixed create mode 100644 tests/ui/doc/doc_markdown-issue_13097.rs create mode 100644 tests/ui/doc/doc_markdown-issue_13097.stderr diff --git a/tests/ui/doc/doc_markdown-issue_13097.fixed b/tests/ui/doc/doc_markdown-issue_13097.fixed new file mode 100644 index 0000000000000..fb0f40b34a4b8 --- /dev/null +++ b/tests/ui/doc/doc_markdown-issue_13097.fixed @@ -0,0 +1,13 @@ +// This test checks that words starting with capital letters and ending with "ified" don't +// trigger the lint. + +#![deny(clippy::doc_markdown)] + +pub enum OutputFormat { + /// `HumaNified` + //~^ ERROR: item in documentation is missing backticks + Plain, + // Should not warn! + /// JSONified console output + Json, +} diff --git a/tests/ui/doc/doc_markdown-issue_13097.rs b/tests/ui/doc/doc_markdown-issue_13097.rs new file mode 100644 index 0000000000000..8c1e1a3cd6c24 --- /dev/null +++ b/tests/ui/doc/doc_markdown-issue_13097.rs @@ -0,0 +1,13 @@ +// This test checks that words starting with capital letters and ending with "ified" don't +// trigger the lint. + +#![deny(clippy::doc_markdown)] + +pub enum OutputFormat { + /// HumaNified + //~^ ERROR: item in documentation is missing backticks + Plain, + // Should not warn! + /// JSONified console output + Json, +} diff --git a/tests/ui/doc/doc_markdown-issue_13097.stderr b/tests/ui/doc/doc_markdown-issue_13097.stderr new file mode 100644 index 0000000000000..ae68a767ec930 --- /dev/null +++ b/tests/ui/doc/doc_markdown-issue_13097.stderr @@ -0,0 +1,18 @@ +error: item in documentation is missing backticks + --> tests/ui/doc/doc_markdown-issue_13097.rs:7:9 + | +LL | /// HumaNified + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/doc/doc_markdown-issue_13097.rs:4:9 + | +LL | #![deny(clippy::doc_markdown)] + | ^^^^^^^^^^^^^^^^^^^^ +help: try + | +LL | /// `HumaNified` + | ~~~~~~~~~~~~ + +error: aborting due to 1 previous error + From 855a9d1377a2da3e4566de9adb8b783295a771dd Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Mon, 24 Jun 2024 18:42:23 +0200 Subject: [PATCH 03/54] Add new `too_long_first_doc_paragraph` first paragraph lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/doc/mod.rs | 113 +++++++++++++----- .../src/doc/too_long_first_doc_paragraph.rs | 85 +++++++++++++ .../ui/too_long_first_doc_paragraph-fix.fixed | 8 ++ tests/ui/too_long_first_doc_paragraph-fix.rs | 7 ++ .../too_long_first_doc_paragraph-fix.stderr | 19 +++ tests/ui/too_long_first_doc_paragraph.rs | 47 ++++++++ tests/ui/too_long_first_doc_paragraph.stderr | 22 ++++ 9 files changed, 270 insertions(+), 33 deletions(-) create mode 100644 clippy_lints/src/doc/too_long_first_doc_paragraph.rs create mode 100644 tests/ui/too_long_first_doc_paragraph-fix.fixed create mode 100644 tests/ui/too_long_first_doc_paragraph-fix.rs create mode 100644 tests/ui/too_long_first_doc_paragraph-fix.stderr create mode 100644 tests/ui/too_long_first_doc_paragraph.rs create mode 100644 tests/ui/too_long_first_doc_paragraph.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 60c03b03d9be3..ca70da5bb5170 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5913,6 +5913,7 @@ Released 2018-09-13 [`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args [`to_string_trait_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_trait_impl [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo +[`too_long_first_doc_paragraph`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_long_first_doc_paragraph [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines [`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 69f9eb6842bcd..2933a65a71f58 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -148,6 +148,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::doc::NEEDLESS_DOCTEST_MAIN_INFO, crate::doc::SUSPICIOUS_DOC_COMMENTS_INFO, crate::doc::TEST_ATTR_IN_DOCTEST_INFO, + crate::doc::TOO_LONG_FIRST_DOC_PARAGRAPH_INFO, crate::doc::UNNECESSARY_SAFETY_DOC_INFO, crate::double_parens::DOUBLE_PARENS_INFO, crate::drop_forget_ref::DROP_NON_DROP_INFO, diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 5b6a5b08aa94c..5e9fb0162bf80 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -1,4 +1,6 @@ mod lazy_continuation; +mod too_long_first_doc_paragraph; + use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; @@ -422,6 +424,38 @@ declare_clippy_lint! { "require every line of a paragraph to be indented and marked" } +declare_clippy_lint! { + /// ### What it does + /// Checks if the first line in the documentation of items listed in module page is too long. + /// + /// ### Why is this bad? + /// Documentation will show the first paragraph of the doscstring in the summary page of a + /// module, so having a nice, short summary in the first paragraph is part of writing good docs. + /// + /// ### Example + /// ```no_run + /// /// A very short summary. + /// /// A much longer explanation that goes into a lot more detail about + /// /// how the thing works, possibly with doclinks and so one, + /// /// and probably spanning a many rows. + /// struct Foo {} + /// ``` + /// Use instead: + /// ```no_run + /// /// A very short summary. + /// /// + /// /// A much longer explanation that goes into a lot more detail about + /// /// how the thing works, possibly with doclinks and so one, + /// /// and probably spanning a many rows. + /// struct Foo {} + /// ``` + #[clippy::version = "1.81.0"] + pub TOO_LONG_FIRST_DOC_PARAGRAPH, + style, + "ensure that the first line of a documentation paragraph isn't too long" +} + +#[derive(Clone)] pub struct Documentation { valid_idents: &'static FxHashSet<String>, check_private_items: bool, @@ -448,6 +482,7 @@ impl_lint_pass!(Documentation => [ SUSPICIOUS_DOC_COMMENTS, EMPTY_DOCS, DOC_LAZY_CONTINUATION, + TOO_LONG_FIRST_DOC_PARAGRAPH, ]); impl<'tcx> LateLintPass<'tcx> for Documentation { @@ -457,39 +492,44 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { }; match cx.tcx.hir_node(cx.last_node_with_lint_attrs) { - Node::Item(item) => match item.kind { - ItemKind::Fn(sig, _, body_id) => { - if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { - let body = cx.tcx.hir().body(body_id); - - let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); - missing_headers::check( + Node::Item(item) => { + too_long_first_doc_paragraph::check(cx, attrs, item.kind, headers.first_paragraph_len); + match item.kind { + ItemKind::Fn(sig, _, body_id) => { + if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) + || in_external_macro(cx.tcx.sess, item.span)) + { + let body = cx.tcx.hir().body(body_id); + + let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); + missing_headers::check( + cx, + item.owner_id, + sig, + headers, + Some(body_id), + panic_info, + self.check_private_items, + ); + } + }, + ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) { + (false, Safety::Unsafe) => span_lint( cx, - item.owner_id, - sig, - headers, - Some(body_id), - panic_info, - self.check_private_items, - ); - } - }, - ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) { - (false, Safety::Unsafe) => span_lint( - cx, - MISSING_SAFETY_DOC, - cx.tcx.def_span(item.owner_id), - "docs for unsafe trait missing `# Safety` section", - ), - (true, Safety::Safe) => span_lint( - cx, - UNNECESSARY_SAFETY_DOC, - cx.tcx.def_span(item.owner_id), - "docs for safe trait have unnecessary `# Safety` section", - ), + MISSING_SAFETY_DOC, + cx.tcx.def_span(item.owner_id), + "docs for unsafe trait missing `# Safety` section", + ), + (true, Safety::Safe) => span_lint( + cx, + UNNECESSARY_SAFETY_DOC, + cx.tcx.def_span(item.owner_id), + "docs for safe trait have unnecessary `# Safety` section", + ), + _ => (), + }, _ => (), - }, - _ => (), + } }, Node::TraitItem(trait_item) => { if let TraitItemKind::Fn(sig, ..) = trait_item.kind @@ -547,6 +587,7 @@ struct DocHeaders { safety: bool, errors: bool, panics: bool, + first_paragraph_len: usize, } /// Does some pre-processing on raw, desugared `#[doc]` attributes such as parsing them and @@ -586,8 +627,9 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[ acc }); doc.pop(); + let doc = doc.trim(); - if doc.trim().is_empty() { + if doc.is_empty() { if let Some(span) = span_of_fragments(&fragments) { span_lint_and_help( cx, @@ -611,7 +653,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[ cx, valid_idents, parser.into_offset_iter(), - &doc, + doc, Fragments { fragments: &fragments, doc: &doc, @@ -653,6 +695,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize let mut paragraph_range = 0..0; let mut code_level = 0; let mut blockquote_level = 0; + let mut is_first_paragraph = true; let mut containers = Vec::new(); @@ -720,6 +763,10 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize } ticks_unbalanced = false; paragraph_range = range; + if is_first_paragraph { + headers.first_paragraph_len = doc[paragraph_range.clone()].chars().count(); + is_first_paragraph = false; + } }, End(TagEnd::Heading(_) | TagEnd::Paragraph | TagEnd::Item) => { if let End(TagEnd::Heading(_)) = event { diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs new file mode 100644 index 0000000000000..a5a58b444011a --- /dev/null +++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -0,0 +1,85 @@ +use rustc_ast::ast::Attribute; +use rustc_errors::Applicability; +use rustc_hir::ItemKind; +use rustc_lint::LateContext; + +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::source::snippet_opt; + +use super::TOO_LONG_FIRST_DOC_PARAGRAPH; + +pub(super) fn check( + cx: &LateContext<'_>, + attrs: &[Attribute], + item_kind: ItemKind<'_>, + mut first_paragraph_len: usize, +) { + if first_paragraph_len <= 100 + || !matches!( + item_kind, + ItemKind::Static(..) + | ItemKind::Const(..) + | ItemKind::Fn(..) + | ItemKind::Macro(..) + | ItemKind::Mod(..) + | ItemKind::TyAlias(..) + | ItemKind::Enum(..) + | ItemKind::Struct(..) + | ItemKind::Union(..) + | ItemKind::Trait(..) + | ItemKind::TraitAlias(..) + ) + { + return; + } + let mut spans = Vec::new(); + let mut should_suggest_empty_doc = false; + + for attr in attrs { + if let Some(doc) = attr.doc_str() { + spans.push(attr.span); + let doc = doc.as_str(); + let doc = doc.trim(); + if spans.len() == 1 { + // We make this suggestion only if the first doc line ends with a punctuation + // because if might just need to add an empty line with `///`. + should_suggest_empty_doc = doc.ends_with('.') || doc.ends_with('!') || doc.ends_with('?'); + } + let len = doc.chars().count(); + if len >= first_paragraph_len { + break; + } + first_paragraph_len -= len; + } + } + + let &[first_span, .., last_span] = spans.as_slice() else { return }; + + if should_suggest_empty_doc + && let Some(second_span) = spans.get(1) + && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi()) + && let Some(snippet) = snippet_opt(cx, new_span) + { + span_lint_and_then( + cx, + TOO_LONG_FIRST_DOC_PARAGRAPH, + first_span.with_hi(last_span.lo()), + "first doc comment paragraph is too long", + |diag| { + diag.span_suggestion( + new_span, + "add", + format!("{snippet}///\n"), + Applicability::MachineApplicable, + ); + }, + ); + return; + } + span_lint( + cx, + TOO_LONG_FIRST_DOC_PARAGRAPH, + first_span.with_hi(last_span.lo()), + "first doc comment paragraph is too long", + ); +} diff --git a/tests/ui/too_long_first_doc_paragraph-fix.fixed b/tests/ui/too_long_first_doc_paragraph-fix.fixed new file mode 100644 index 0000000000000..b3f66f5279384 --- /dev/null +++ b/tests/ui/too_long_first_doc_paragraph-fix.fixed @@ -0,0 +1,8 @@ +#![warn(clippy::too_long_first_doc_paragraph)] + +/// A very short summary. +/// +/// A much longer explanation that goes into a lot more detail about +/// how the thing works, possibly with doclinks and so one, +/// and probably spanning a many rows. +struct Foo; diff --git a/tests/ui/too_long_first_doc_paragraph-fix.rs b/tests/ui/too_long_first_doc_paragraph-fix.rs new file mode 100644 index 0000000000000..f0ece9523de4b --- /dev/null +++ b/tests/ui/too_long_first_doc_paragraph-fix.rs @@ -0,0 +1,7 @@ +#![warn(clippy::too_long_first_doc_paragraph)] + +/// A very short summary. +/// A much longer explanation that goes into a lot more detail about +/// how the thing works, possibly with doclinks and so one, +/// and probably spanning a many rows. +struct Foo; diff --git a/tests/ui/too_long_first_doc_paragraph-fix.stderr b/tests/ui/too_long_first_doc_paragraph-fix.stderr new file mode 100644 index 0000000000000..00949f405d538 --- /dev/null +++ b/tests/ui/too_long_first_doc_paragraph-fix.stderr @@ -0,0 +1,19 @@ +error: first doc comment paragraph is too long + --> tests/ui/too_long_first_doc_paragraph-fix.rs:3:1 + | +LL | / /// A very short summary. +LL | | /// A much longer explanation that goes into a lot more detail about +LL | | /// how the thing works, possibly with doclinks and so one, +LL | | /// and probably spanning a many rows. + | |_ + | + = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]` +help: add an empty line + | +LL ~ /// A very short summary. +LL + /// + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/too_long_first_doc_paragraph.rs b/tests/ui/too_long_first_doc_paragraph.rs new file mode 100644 index 0000000000000..88a8f6d38310d --- /dev/null +++ b/tests/ui/too_long_first_doc_paragraph.rs @@ -0,0 +1,47 @@ +//@no-rustfix + +#![warn(clippy::too_long_first_doc_paragraph)] + +/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia +/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, +/// gravida non lacinia at, rhoncus eu lacus. +pub struct Bar; + +// Should not warn! (not an item visible on mod page) +/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia +/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, +/// gravida non lacinia at, rhoncus eu lacus. +impl Bar {} + +// Should not warn! (less than 80 characters) +/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. +/// +/// Nunc turpis nunc, lacinia +/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, +/// gravida non lacinia at, rhoncus eu lacus. +enum Enum { + A, +} + +/// Lorem +/// ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia +/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, +/// gravida non lacinia at, rhoncus eu lacus. +union Union { + a: u8, + b: u8, +} + +// Should not warn! (title) +/// # bla +/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia +/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, +/// gravida non lacinia at, rhoncus eu lacus. +union Union2 { + a: u8, + b: u8, +} + +fn main() { + // test code goes here +} diff --git a/tests/ui/too_long_first_doc_paragraph.stderr b/tests/ui/too_long_first_doc_paragraph.stderr new file mode 100644 index 0000000000000..7f48e5cf884e6 --- /dev/null +++ b/tests/ui/too_long_first_doc_paragraph.stderr @@ -0,0 +1,22 @@ +error: first doc comment paragraph is too long + --> tests/ui/too_long_first_doc_paragraph.rs:5:1 + | +LL | / /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia +LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, +LL | | /// gravida non lacinia at, rhoncus eu lacus. + | |_ + | + = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]` + +error: first doc comment paragraph is too long + --> tests/ui/too_long_first_doc_paragraph.rs:26:1 + | +LL | / /// Lorem +LL | | /// ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia +LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, +LL | | /// gravida non lacinia at, rhoncus eu lacus. + | |_ + +error: aborting due to 2 previous errors + From f455587feec19b459c5eb75c9dc1995b93bb24f6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Tue, 9 Jul 2024 16:00:06 +0200 Subject: [PATCH 04/54] Set the limit of characters to 200 and don't run the lint on private items unless config allows it --- clippy_lints/src/doc/mod.rs | 13 +++++++++---- .../src/doc/too_long_first_doc_paragraph.rs | 17 +++++++++++------ tests/ui/too_long_first_doc_paragraph-fix.fixed | 5 +++-- tests/ui/too_long_first_doc_paragraph-fix.rs | 5 +++-- .../ui/too_long_first_doc_paragraph-fix.stderr | 3 ++- tests/ui/too_long_first_doc_paragraph.rs | 12 +++++++++--- 6 files changed, 37 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 5e9fb0162bf80..6debcd9b6a0f1 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -493,7 +493,13 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { match cx.tcx.hir_node(cx.last_node_with_lint_attrs) { Node::Item(item) => { - too_long_first_doc_paragraph::check(cx, attrs, item.kind, headers.first_paragraph_len); + too_long_first_doc_paragraph::check( + cx, + item, + attrs, + headers.first_paragraph_len, + self.check_private_items, + ); match item.kind { ItemKind::Fn(sig, _, body_id) => { if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) @@ -627,9 +633,8 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[ acc }); doc.pop(); - let doc = doc.trim(); - if doc.is_empty() { + if doc.trim().is_empty() { if let Some(span) = span_of_fragments(&fragments) { span_lint_and_help( cx, @@ -653,7 +658,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[ cx, valid_idents, parser.into_offset_iter(), - doc, + &doc, Fragments { fragments: &fragments, doc: &doc, diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index a5a58b444011a..a1b714b7d0020 100644 --- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -1,6 +1,6 @@ use rustc_ast::ast::Attribute; use rustc_errors::Applicability; -use rustc_hir::ItemKind; +use rustc_hir::{Item, ItemKind}; use rustc_lint::LateContext; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; @@ -10,13 +10,17 @@ use super::TOO_LONG_FIRST_DOC_PARAGRAPH; pub(super) fn check( cx: &LateContext<'_>, + item: &Item<'_>, attrs: &[Attribute], - item_kind: ItemKind<'_>, mut first_paragraph_len: usize, + check_private_items: bool, ) { - if first_paragraph_len <= 100 + if !check_private_items && !cx.effective_visibilities.is_exported(item.owner_id.def_id) { + return; + } + if first_paragraph_len <= 200 || !matches!( - item_kind, + item.kind, ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::Fn(..) @@ -32,6 +36,7 @@ pub(super) fn check( { return; } + let mut spans = Vec::new(); let mut should_suggest_empty_doc = false; @@ -42,7 +47,7 @@ pub(super) fn check( let doc = doc.trim(); if spans.len() == 1 { // We make this suggestion only if the first doc line ends with a punctuation - // because if might just need to add an empty line with `///`. + // because it might just need to add an empty line with `///`. should_suggest_empty_doc = doc.ends_with('.') || doc.ends_with('!') || doc.ends_with('?'); } let len = doc.chars().count(); @@ -68,7 +73,7 @@ pub(super) fn check( |diag| { diag.span_suggestion( new_span, - "add", + "add an empty line", format!("{snippet}///\n"), Applicability::MachineApplicable, ); diff --git a/tests/ui/too_long_first_doc_paragraph-fix.fixed b/tests/ui/too_long_first_doc_paragraph-fix.fixed index b3f66f5279384..d4a0cdf3447f1 100644 --- a/tests/ui/too_long_first_doc_paragraph-fix.fixed +++ b/tests/ui/too_long_first_doc_paragraph-fix.fixed @@ -4,5 +4,6 @@ /// /// A much longer explanation that goes into a lot more detail about /// how the thing works, possibly with doclinks and so one, -/// and probably spanning a many rows. -struct Foo; +/// and probably spanning a many rows. Blablabla, it needs to be over +/// 200 characters so I needed to write something longeeeeeeer. +pub struct Foo; diff --git a/tests/ui/too_long_first_doc_paragraph-fix.rs b/tests/ui/too_long_first_doc_paragraph-fix.rs index f0ece9523de4b..5a3b6c42a328b 100644 --- a/tests/ui/too_long_first_doc_paragraph-fix.rs +++ b/tests/ui/too_long_first_doc_paragraph-fix.rs @@ -3,5 +3,6 @@ /// A very short summary. /// A much longer explanation that goes into a lot more detail about /// how the thing works, possibly with doclinks and so one, -/// and probably spanning a many rows. -struct Foo; +/// and probably spanning a many rows. Blablabla, it needs to be over +/// 200 characters so I needed to write something longeeeeeeer. +pub struct Foo; diff --git a/tests/ui/too_long_first_doc_paragraph-fix.stderr b/tests/ui/too_long_first_doc_paragraph-fix.stderr index 00949f405d538..6403265a39c54 100644 --- a/tests/ui/too_long_first_doc_paragraph-fix.stderr +++ b/tests/ui/too_long_first_doc_paragraph-fix.stderr @@ -4,7 +4,8 @@ error: first doc comment paragraph is too long LL | / /// A very short summary. LL | | /// A much longer explanation that goes into a lot more detail about LL | | /// how the thing works, possibly with doclinks and so one, -LL | | /// and probably spanning a many rows. +LL | | /// and probably spanning a many rows. Blablabla, it needs to be over +LL | | /// 200 characters so I needed to write something longeeeeeeer. | |_ | = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings` diff --git a/tests/ui/too_long_first_doc_paragraph.rs b/tests/ui/too_long_first_doc_paragraph.rs index 88a8f6d38310d..1042249c5b7bd 100644 --- a/tests/ui/too_long_first_doc_paragraph.rs +++ b/tests/ui/too_long_first_doc_paragraph.rs @@ -19,7 +19,7 @@ impl Bar {} /// Nunc turpis nunc, lacinia /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, /// gravida non lacinia at, rhoncus eu lacus. -enum Enum { +pub enum Enum { A, } @@ -27,7 +27,7 @@ enum Enum { /// ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, /// gravida non lacinia at, rhoncus eu lacus. -union Union { +pub union Union { a: u8, b: u8, } @@ -37,11 +37,17 @@ union Union { /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, /// gravida non lacinia at, rhoncus eu lacus. -union Union2 { +pub union Union2 { a: u8, b: u8, } +// Should not warn! (not public) +/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia +/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, +/// gravida non lacinia at, rhoncus eu lacus. +fn f() {} + fn main() { // test code goes here } From 4969960a9c0a9ac4e8bbe0083124186eb5159028 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Tue, 9 Jul 2024 16:29:39 +0200 Subject: [PATCH 05/54] Fix dogfood lints --- .../src/doc/too_long_first_doc_paragraph.rs | 4 +++- clippy_utils/src/lib.rs | 21 ++++++++++++------- clippy_utils/src/macros.rs | 9 ++++---- clippy_utils/src/source.rs | 14 +++++++------ clippy_utils/src/ty.rs | 20 +++++++++++------- 5 files changed, 42 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index a1b714b7d0020..45ec392d55380 100644 --- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -58,7 +58,9 @@ pub(super) fn check( } } - let &[first_span, .., last_span] = spans.as_slice() else { return }; + let &[first_span, .., last_span] = spans.as_slice() else { + return; + }; if should_suggest_empty_doc && let Some(second_span) = spans.get(1) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1d5f1a2a2bb13..7d613a50665f4 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -150,6 +150,7 @@ macro_rules! extract_msrv_attr { /// If the given expression is a local binding, find the initializer expression. /// If that initializer expression is another local binding, find its initializer again. +/// /// This process repeats as long as possible (but usually no more than once). Initializer /// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`] /// instead. @@ -180,6 +181,7 @@ pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr } /// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable. +/// /// By only considering immutable bindings, we guarantee that the returned expression represents the /// value of the binding wherever it is referenced. /// @@ -428,12 +430,12 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tc }) } -/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the +/// THIS METHOD IS DEPRECATED. Matches a `QPath` against a slice of segment string literals. +/// +/// This method is deprecated and will eventually be removed since it does not match against the /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from /// `QPath::Resolved.1.res.opt_def_id()`. /// -/// Matches a `QPath` against a slice of segment string literals. -/// /// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a /// `rustc_hir::QPath`. /// @@ -482,12 +484,12 @@ pub fn is_path_diagnostic_item<'tcx>( path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id)) } -/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the +/// THIS METHOD IS DEPRECATED. Matches a `Path` against a slice of segment string literals. +/// +/// This method is deprecated and will eventually be removed since it does not match against the /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from /// `QPath::Resolved.1.res.opt_def_id()`. /// -/// Matches a `Path` against a slice of segment string literals. -/// /// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a /// `rustc_hir::Path`. /// @@ -903,6 +905,7 @@ pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> } /// Returns true if the expr is equal to `Default::default()` of it's type when evaluated. +/// /// It doesn't cover all cases, for example indirect function calls (some of std /// functions are supported) but it is the best we have. pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { @@ -1059,6 +1062,7 @@ impl std::ops::BitOrAssign for CaptureKind { } /// Given an expression referencing a local, determines how it would be captured in a closure. +/// /// Note as this will walk up to parent expressions until the capture can be determined it should /// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or /// function argument (other than a receiver). @@ -2361,8 +2365,9 @@ pub fn fn_def_id_with_node_args<'tcx>( } /// Returns `Option<String>` where String is a textual representation of the type encapsulated in -/// the slice iff the given expression is a slice of primitives (as defined in the -/// `is_recursively_primitive_type` function) and `None` otherwise. +/// the slice iff the given expression is a slice of primitives. +/// +/// (As defined in the `is_recursively_primitive_type` function.) Returns `None` otherwise. pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> { let expr_type = cx.typeck_results().expr_ty_adjusted(expr); let expr_kind = expr_type.kind(); diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 455239cc37f35..3c85eb1296e1f 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -150,10 +150,11 @@ pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) -> } /// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the -/// macro call site (i.e. the parent of the macro expansion). This generally means that `node` -/// is the outermost node of an entire macro expansion, but there are some caveats noted below. -/// This is useful for finding macro calls while visiting the HIR without processing the macro call -/// at every node within its expansion. +/// macro call site (i.e. the parent of the macro expansion). +/// +/// This generally means that `node` is the outermost node of an entire macro expansion, but there +/// are some caveats noted below. This is useful for finding macro calls while visiting the HIR +/// without processing the macro call at every node within its expansion. /// /// If you already have immediate access to the parent node, it is simpler to /// just check the context of that span directly (e.g. `parent.span.from_expansion()`). diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 496c8f5b55373..924cdf1b8735c 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -527,9 +527,10 @@ pub fn snippet_block_with_context<'a>( (reindent_multiline(snip, true, indent), from_macro) } -/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This -/// will result in the macro call, rather than the expansion, if the span is from a child context. -/// If the span is not from a child context, it will be used directly instead. +/// Same as `snippet_with_applicability`, but first walks the span up to the given context. +/// +/// This will result in the macro call, rather than the expansion, if the span is from a child +/// context. If the span is not from a child context, it will be used directly instead. /// /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node /// would result in `box []`. If given the context of the address of expression, this function will @@ -572,9 +573,10 @@ fn snippet_with_context_sess<'a>( } /// Walks the span up to the target context, thereby returning the macro call site if the span is -/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the -/// case of the span being in a macro expansion, but the target context is from expanding a macro -/// argument. +/// inside a macro expansion, or the original span if it is not. +/// +/// Note this will return `None` in the case of the span being in a macro expansion, but the target +/// context is from expanding a macro argument. /// /// Given the following /// diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 812fb647fdab6..de5ae484834cc 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -160,8 +160,10 @@ pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symb } /// Returns true if `ty` is a type on which calling `Clone` through a function instead of -/// as a method, such as `Arc::clone()` is considered idiomatic. Lints should avoid suggesting to -/// replace instances of `ty::Clone()` by `.clone()` for objects of those types. +/// as a method, such as `Arc::clone()` is considered idiomatic. +/// +/// Lints should avoid suggesting to replace instances of `ty::Clone()` by `.clone()` for objects +/// of those types. pub fn should_call_clone_as_function(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { matches!( get_type_diagnostic_name(cx, ty), @@ -398,8 +400,10 @@ fn is_normalizable_helper<'tcx>( } /// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any -/// integer or floating-point number type). For checking aggregation of primitive types (e.g. -/// tuples and slices of primitive type) see `is_recursively_primitive_type` +/// integer or floating-point number type). +/// +/// For checking aggregation of primitive types (e.g. tuples and slices of primitive type) see +/// `is_recursively_primitive_type` pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool { matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_)) } @@ -476,9 +480,10 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { } } -/// Checks if the drop order for a type matters. Some std types implement drop solely to -/// deallocate memory. For these types, and composites containing them, changing the drop order -/// won't result in any observable side effects. +/// Checks if the drop order for a type matters. +/// +/// Some std types implement drop solely to deallocate memory. For these types, and composites +/// containing them, changing the drop order won't result in any observable side effects. pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool { if !seen.insert(ty) { @@ -1324,6 +1329,7 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl } /// Checks if a Ty<'_> has some inherent method Symbol. +/// /// This does not look for impls in the type's `Deref::Target` type. /// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`. pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> { From 3c6e5ef4ae20c7439a2bc951ef349c59954412c6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Sun, 21 Jul 2024 17:29:32 +0200 Subject: [PATCH 06/54] Add comment explaining what the matching items are for `TOO_LONG_FIRST_DOC_PARAGRAPH` lint --- clippy_lints/src/doc/too_long_first_doc_paragraph.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index 45ec392d55380..a47cba64b28e5 100644 --- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -21,6 +21,8 @@ pub(super) fn check( if first_paragraph_len <= 200 || !matches!( item.kind, + // This is the list of items which can be documented AND are displayed on the module + // page. So associated items or impl blocks are not part of this list. ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::Fn(..) From b7e7975dedb42edf3ba1b53708950db34c31c182 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Tue, 30 Jul 2024 16:01:36 +0200 Subject: [PATCH 07/54] Move style into its file --- .github/deploy.sh | 1 + util/gh-pages/index.html | 366 +-------------------------------------- util/gh-pages/style.css | 364 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 366 insertions(+), 365 deletions(-) create mode 100644 util/gh-pages/style.css diff --git a/.github/deploy.sh b/.github/deploy.sh index 5a59f94ec918b..d937661c0f828 100644 --- a/.github/deploy.sh +++ b/.github/deploy.sh @@ -10,6 +10,7 @@ mkdir out/master/ cp util/gh-pages/index.html out/master cp util/gh-pages/script.js out/master cp util/gh-pages/lints.json out/master +cp util/gh-pages/style.css out/master if [[ -n $TAG_NAME ]]; then echo "Save the doc for the current tag ($TAG_NAME) and point stable/ to it" diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 7f271ac838597..267354cc8bfc6 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -23,371 +23,7 @@ <link id="styleHighlight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/highlight.css"> <link id="styleNight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/tomorrow-night.css" disabled="true"> <link id="styleAyu" rel="stylesheet" href="https://rust-lang.github.io/mdBook/ayu-highlight.css" disabled="true"> - <style> - blockquote { font-size: 1em; } - [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; } - - .dropdown-menu { - color: var(--fg); - background: var(--theme-popup-bg); - border: 1px solid var(--theme-popup-border); - } - - .dropdown-menu .divider { - background-color: var(--theme-popup-border); - } - - .dropdown-menu .checkbox { - display: block; - white-space: nowrap; - margin: 0; - } - .dropdown-menu .checkbox label { - padding: 3px 20px; - width: 100%; - } - - .dropdown-menu .checkbox input { - position: relative; - margin: 0 0.5rem 0; - padding: 0; - } - - .dropdown-menu .checkbox:hover { - background-color: var(--theme-hover); - } - - div.panel div.panel-body button { - background: var(--searchbar-bg); - color: var(--searchbar-fg); - border-color: var(--theme-popup-border); - } - - div.panel div.panel-body button:hover { - box-shadow: 0 0 3px var(--searchbar-shadow-color); - } - - div.panel div.panel-body button.open { - filter: brightness(90%); - } - - .dropdown-toggle .badge { - background-color: #777; - } - - .panel-heading { cursor: pointer; } - - .panel-title { display: flex; flex-wrap: wrap;} - .panel-title .label { display: inline-block; } - - .panel-title-name { flex: 1; min-width: 400px;} - .panel-title-name span { vertical-align: bottom; } - - .panel .panel-title-name .anchor { display: none; } - .panel:hover .panel-title-name .anchor { display: inline;} - - .search-control { - margin-top: 15px; - } - - @media (min-width: 992px) { - .search-control { - margin-top: 0; - } - } - - @media (min-width: 405px) { - #upper-filters { - display: flex; - flex-wrap: wrap; - } - } - - @media (max-width: 430px) { - /* Turn the version filter list to the left */ - #version-filter-selector { - right: 0; - left: auto; - } - } - - @media (max-width: 412px) { - #upper-filters, - .panel-body .search-control { - padding-right: 8px; - padding-left: 8px; - } - } - - .label { - padding-top: 0.3em; - padding-bottom: 0.3em; - } - - .label-lint-group { - min-width: 8em; - } - .label-lint-level { - min-width: 4em; - } - - .label-lint-level-allow { - background-color: #5cb85c; - } - .label-lint-level-warn { - background-color: #f0ad4e; - } - .label-lint-level-deny { - background-color: #d9534f; - } - .label-lint-level-none { - background-color: #777777; - opacity: 0.5; - } - - .label-group-deprecated { - opacity: 0.5; - } - - .label-doc-folding { - color: #000; - background-color: #fff; - border: 1px solid var(--theme-popup-border); - } - .label-doc-folding:hover { - background-color: #e6e6e6; - } - - .lint-doc-md > h3 { - border-top: 1px solid var(--theme-popup-border); - padding: 10px 15px; - margin: 0 -15px; - font-size: 18px; - } - .lint-doc-md > h3:first-child { - border-top: none; - padding-top: 0px; - } - - @media (max-width:749px) { - .lint-additional-info-container { - display: flex; - flex-flow: column; - } - .lint-additional-info-item + .lint-additional-info-item { - border-top: 1px solid var(--theme-popup-border); - } - } - @media (min-width:750px) { - .lint-additional-info-container { - display: flex; - flex-flow: row; - } - .lint-additional-info-item + .lint-additional-info-item { - border-left: 1px solid var(--theme-popup-border); - } - } - - .lint-additional-info-item { - display: inline-flex; - min-width: 200px; - flex-grow: 1; - padding: 9px 5px 5px 15px; - } - - .label-applicability { - background-color: #777777; - margin: auto 5px; - } - - .label-version { - background-color: #777777; - margin: auto 5px; - font-family: monospace; - } - - details { - border-radius: 4px; - padding: .5em .5em 0; - } - - code { - white-space: pre !important; - } - - summary { - font-weight: bold; - margin: -.5em -.5em 0; - padding: .5em; - display: revert; - } - - details[open] { - padding: .5em; - } - </style> - <style> - /* Expanding the mdBoom theme*/ - .light { - --inline-code-bg: #f6f7f6; - } - .rust { - --inline-code-bg: #f6f7f6; - } - .coal { - --inline-code-bg: #1d1f21; - } - .navy { - --inline-code-bg: #1d1f21; - } - .ayu { - --inline-code-bg: #191f26; - } - - .theme-dropdown { - position: absolute; - margin: 0.7em; - z-index: 10; - } - - /* Applying the mdBook theme */ - .theme-icon { - text-align: center; - width: 2em; - height: 2em; - line-height: 2em; - border: solid 1px var(--icons); - border-radius: 5px; - user-select: none; - cursor: pointer; - } - .theme-icon:hover { - background: var(--theme-hover); - } - .theme-choice { - display: none; - list-style: none; - border: 1px solid var(--theme-popup-border); - border-radius: 5px; - color: var(--fg); - background: var(--theme-popup-bg); - padding: 0 0; - overflow: hidden; - } - - .theme-dropdown.open .theme-choice { - display: block; - } - - .theme-choice > li { - padding: 5px 10px; - font-size: 0.8em; - user-select: none; - cursor: pointer; - } - - .theme-choice > li:hover { - background: var(--theme-hover); - } - - .alert { - color: var(--fg); - background: var(--theme-hover); - border: 1px solid var(--theme-popup-border); - } - .page-header { - border-color: var(--theme-popup-border); - } - .panel-default > .panel-heading { - background: var(--theme-hover); - color: var(--fg); - border: 1px solid var(--theme-popup-border); - } - .panel-default > .panel-heading:hover { - filter: brightness(90%); - } - .list-group-item { - background: 0%; - border: 1px solid var(--theme-popup-border); - } - .panel, pre, hr { - background: var(--bg); - border: 1px solid var(--theme-popup-border); - } - - #version-filter-selector .checkbox { - display: flex; - } - - #version-filter { - min-width: available; - } - - #version-filter li label { - padding-right: 0; - width: 35%; - } - - .version-filter-input { - height: 60%; - width: 30%; - text-align: center; - border: none; - border-bottom: 1px solid #000000; - } - - #filter-label, .filter-clear { - background: var(--searchbar-bg); - color: var(--searchbar-fg); - border-color: var(--theme-popup-border); - filter: brightness(95%); - } - #filter-label:hover, .filter-clear:hover { - filter: brightness(90%); - } - .filter-input { - background: var(--searchbar-bg); - color: var(--searchbar-fg); - border-color: var(--theme-popup-border); - } - - .filter-input::-webkit-input-placeholder, - .filter-input::-moz-placeholder { - color: var(--searchbar-fg); - opacity: 30%; - } - - .expansion-group { - margin-top: 15px; - padding: 0px 8px; - display: flex; - flex-wrap: nowrap; - } - - @media (min-width: 992px) { - .expansion-group { - margin-top: 0; - padding: 0px 15px; - } - } - - .expansion-control { - width: 50%; - } - - :not(pre) > code { - color: var(--inline-code-color); - background-color: var(--inline-code-bg); - } - html { - scrollbar-color: var(--scrollbar) var(--bg); - } - body { - background: var(--bg); - color: var(--fg); - } - - </style> + <link rel="stylesheet" href="style.css"> </head> <body ng-app="clippy" ng-controller="lintList"> <div theme-dropdown class="theme-dropdown"> diff --git a/util/gh-pages/style.css b/util/gh-pages/style.css new file mode 100644 index 0000000000000..f9feb0ba13ae3 --- /dev/null +++ b/util/gh-pages/style.css @@ -0,0 +1,364 @@ +blockquote { font-size: 1em; } + +[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { + display: none !important; +} + +.dropdown-menu { + color: var(--fg); + background: var(--theme-popup-bg); + border: 1px solid var(--theme-popup-border); +} + +.dropdown-menu .divider { + background-color: var(--theme-popup-border); +} + +.dropdown-menu .checkbox { + display: block; + white-space: nowrap; + margin: 0; +} +.dropdown-menu .checkbox label { + padding: 3px 20px; + width: 100%; +} + +.dropdown-menu .checkbox input { + position: relative; + margin: 0 0.5rem 0; + padding: 0; +} + +.dropdown-menu .checkbox:hover { + background-color: var(--theme-hover); +} + +div.panel div.panel-body button { + background: var(--searchbar-bg); + color: var(--searchbar-fg); + border-color: var(--theme-popup-border); +} + +div.panel div.panel-body button:hover { + box-shadow: 0 0 3px var(--searchbar-shadow-color); +} + +div.panel div.panel-body button.open { + filter: brightness(90%); +} + +.dropdown-toggle .badge { + background-color: #777; +} + +.panel-heading { cursor: pointer; } + +.panel-title { display: flex; flex-wrap: wrap;} +.panel-title .label { display: inline-block; } + +.panel-title-name { flex: 1; min-width: 400px;} +.panel-title-name span { vertical-align: bottom; } + +.panel .panel-title-name .anchor { display: none; } +.panel:hover .panel-title-name .anchor { display: inline;} + +.search-control { + margin-top: 15px; +} + +@media (min-width: 992px) { + .search-control { + margin-top: 0; + } +} + +@media (min-width: 405px) { + #upper-filters { + display: flex; + flex-wrap: wrap; + } +} + +@media (max-width: 430px) { + /* Turn the version filter list to the left */ + #version-filter-selector { + right: 0; + left: auto; + } +} + +@media (max-width: 412px) { + #upper-filters, + .panel-body .search-control { + padding-right: 8px; + padding-left: 8px; + } +} + +.label { + padding-top: 0.3em; + padding-bottom: 0.3em; +} + +.label-lint-group { + min-width: 8em; +} +.label-lint-level { + min-width: 4em; +} + +.label-lint-level-allow { + background-color: #5cb85c; +} +.label-lint-level-warn { + background-color: #f0ad4e; +} +.label-lint-level-deny { + background-color: #d9534f; +} +.label-lint-level-none { + background-color: #777777; + opacity: 0.5; +} + +.label-group-deprecated { + opacity: 0.5; +} + +.label-doc-folding { + color: #000; + background-color: #fff; + border: 1px solid var(--theme-popup-border); +} +.label-doc-folding:hover { + background-color: #e6e6e6; +} + +.lint-doc-md > h3 { + border-top: 1px solid var(--theme-popup-border); + padding: 10px 15px; + margin: 0 -15px; + font-size: 18px; +} +.lint-doc-md > h3:first-child { + border-top: none; + padding-top: 0px; +} + +@media (max-width:749px) { + .lint-additional-info-container { + display: flex; + flex-flow: column; + } + .lint-additional-info-item + .lint-additional-info-item { + border-top: 1px solid var(--theme-popup-border); + } +} +@media (min-width:750px) { + .lint-additional-info-container { + display: flex; + flex-flow: row; + } + .lint-additional-info-item + .lint-additional-info-item { + border-left: 1px solid var(--theme-popup-border); + } +} + +.lint-additional-info-item { + display: inline-flex; + min-width: 200px; + flex-grow: 1; + padding: 9px 5px 5px 15px; +} + +.label-applicability { + background-color: #777777; + margin: auto 5px; +} + +.label-version { + background-color: #777777; + margin: auto 5px; + font-family: monospace; +} + +details { + border-radius: 4px; + padding: .5em .5em 0; +} + +code { + white-space: pre !important; +} + +summary { + font-weight: bold; + margin: -.5em -.5em 0; + padding: .5em; + display: revert; +} + +details[open] { + padding: .5em; +} + +/* Expanding the mdBook theme*/ +.light { + --inline-code-bg: #f6f7f6; +} +.rust { + --inline-code-bg: #f6f7f6; +} +.coal { + --inline-code-bg: #1d1f21; +} +.navy { + --inline-code-bg: #1d1f21; +} +.ayu { + --inline-code-bg: #191f26; +} + +.theme-dropdown { + position: absolute; + margin: 0.7em; + z-index: 10; +} + +/* Applying the mdBook theme */ +.theme-icon { + text-align: center; + width: 2em; + height: 2em; + line-height: 2em; + border: solid 1px var(--icons); + border-radius: 5px; + user-select: none; + cursor: pointer; +} +.theme-icon:hover { + background: var(--theme-hover); +} +.theme-choice { + display: none; + list-style: none; + border: 1px solid var(--theme-popup-border); + border-radius: 5px; + color: var(--fg); + background: var(--theme-popup-bg); + padding: 0 0; + overflow: hidden; +} + +.theme-dropdown.open .theme-choice { + display: block; +} + +.theme-choice > li { + padding: 5px 10px; + font-size: 0.8em; + user-select: none; + cursor: pointer; +} + +.theme-choice > li:hover { + background: var(--theme-hover); +} + +.alert { + color: var(--fg); + background: var(--theme-hover); + border: 1px solid var(--theme-popup-border); +} +.page-header { + border-color: var(--theme-popup-border); +} +.panel-default > .panel-heading { + background: var(--theme-hover); + color: var(--fg); + border: 1px solid var(--theme-popup-border); +} +.panel-default > .panel-heading:hover { + filter: brightness(90%); +} +.list-group-item { + background: 0%; + border: 1px solid var(--theme-popup-border); +} +.panel, pre, hr { + background: var(--bg); + border: 1px solid var(--theme-popup-border); +} + +#version-filter-selector .checkbox { + display: flex; +} + +#version-filter { + min-width: available; +} + +#version-filter li label { + padding-right: 0; + width: 35%; +} + +.version-filter-input { + height: 60%; + width: 30%; + text-align: center; + border: none; + border-bottom: 1px solid #000000; +} + +#filter-label, .filter-clear { + background: var(--searchbar-bg); + color: var(--searchbar-fg); + border-color: var(--theme-popup-border); + filter: brightness(95%); +} +#filter-label:hover, .filter-clear:hover { + filter: brightness(90%); +} +.filter-input { + background: var(--searchbar-bg); + color: var(--searchbar-fg); + border-color: var(--theme-popup-border); +} + +.filter-input::-webkit-input-placeholder, +.filter-input::-moz-placeholder { + color: var(--searchbar-fg); + opacity: 30%; +} + +.expansion-group { + margin-top: 15px; + padding: 0px 8px; + display: flex; + flex-wrap: nowrap; +} + +@media (min-width: 992px) { + .expansion-group { + margin-top: 0; + padding: 0px 15px; + } +} + +.expansion-control { + width: 50%; +} + +:not(pre) > code { + color: var(--inline-code-color); + background-color: var(--inline-code-bg); +} +html { + scrollbar-color: var(--scrollbar) var(--bg); +} +body { + background: var(--bg); + color: var(--fg); +} From 7c5209121d52411d95bca199124d35ab3300ce9d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Tue, 30 Jul 2024 17:31:42 +0200 Subject: [PATCH 08/54] Add new settings menu --- util/gh-pages/index.html | 27 ++++++++++++++++---- util/gh-pages/script.js | 45 ++++++++++++++++++++++++++++++---- util/gh-pages/style.css | 53 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 110 insertions(+), 15 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 267354cc8bfc6..12c9608f6f5f7 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -26,11 +26,28 @@ <link rel="stylesheet" href="style.css"> </head> <body ng-app="clippy" ng-controller="lintList"> - <div theme-dropdown class="theme-dropdown"> - <div id="theme-icon" class="theme-icon">🖌</div> - <ul id="theme-menu" class="theme-choice"> - <li id="{{id}}" ng-repeat="(id, name) in themes" ng-click="selectTheme(id)">{{name}}</li> - </ul> + <div id="settings"> + <div theme-dropdown class="theme-dropdown"> + <div class="menu-container"> + <div id="theme-icon" class="theme-icon">🖌</div> + <ul id="theme-menu" class="theme-choice"> + <li id="{{id}}" ng-repeat="(id, name) in themes" ng-click="selectTheme(id)">{{name}}</li> + </ul> + </div> + </div> + <div settings-dropdown class="settings-dropdown"> + <div class="menu-container"> + <div id="settings-icon" class="settings-icon"></div> + <ul id="settings-menu" class="settings-choice"> + <li> + <label> + <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)"> + <span>Disable keyboard shortcuts</span> + </label> + </li> + </ul> + </div> + </div> </div> <div class="container"> diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index ed1e090e1b540..f072327bc340f 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -68,6 +68,24 @@ } } }) + .directive('settingsDropdown', function ($document) { + return { + restrict: 'A', + link: function ($scope, $element, $attr) { + $element.bind('click', function () { + $element.toggleClass('open'); + $element.addClass('open-recent'); + }); + + $document.bind('click', function () { + if (!$element.hasClass('open-recent')) { + $element.removeClass('open'); + } + $element.removeClass('open-recent'); + }) + } + } + }) .directive('filterDropdown', function ($document) { return { restrict: 'A', @@ -537,6 +555,16 @@ function getQueryVariable(variable) { } } +function storeValue(settingName, value) { + try { + localStorage.setItem(`clippy-lint-list-${settingName}`, value); + } catch (e) { } +} + +function loadValue(settingName) { + return localStorage.getItem(`clippy-lint-list-${settingName}`); +} + function setTheme(theme, store) { let enableHighlight = false; let enableNight = false; @@ -569,14 +597,12 @@ function setTheme(theme, store) { document.getElementById("styleAyu").disabled = !enableAyu; if (store) { - try { - localStorage.setItem('clippy-lint-list-theme', theme); - } catch (e) { } + storeValue("theme", theme); } } function handleShortcut(ev) { - if (ev.ctrlKey || ev.altKey || ev.metaKey) { + if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) { return; } @@ -601,11 +627,20 @@ function handleShortcut(ev) { document.addEventListener("keypress", handleShortcut); document.addEventListener("keydown", handleShortcut); +function changeSetting(elem) { + if (elem.id === "disable-shortcuts") { + disableShortcuts = elem.checked; + storeValue(elem.id, elem.checked); + } +} + // loading the theme after the initial load const prefersDark = window.matchMedia("(prefers-color-scheme: dark)"); -const theme = localStorage.getItem('clippy-lint-list-theme'); +const theme = loadValue('theme'); if (prefersDark.matches && !theme) { setTheme("coal", false); } else { setTheme(theme, false); } +let disableShortcuts = loadValue('disable-shortcuts') === "true"; +document.getElementById("disable-shortcuts").checked = disableShortcuts; diff --git a/util/gh-pages/style.css b/util/gh-pages/style.css index f9feb0ba13ae3..4ad8b502dd82f 100644 --- a/util/gh-pages/style.css +++ b/util/gh-pages/style.css @@ -220,14 +220,20 @@ details[open] { --inline-code-bg: #191f26; } -.theme-dropdown { +#settings { position: absolute; margin: 0.7em; z-index: 10; + display: flex; +} + +.menu-container { + position: relative; + width: 28px; } /* Applying the mdBook theme */ -.theme-icon { +.theme-icon, .settings-icon { text-align: center; width: 2em; height: 2em; @@ -237,10 +243,10 @@ details[open] { user-select: none; cursor: pointer; } -.theme-icon:hover { +.theme-icon:hover, .settings-icon:hover { background: var(--theme-hover); } -.theme-choice { +.theme-choice, .settings-choice { display: none; list-style: none; border: 1px solid var(--theme-popup-border); @@ -249,9 +255,46 @@ details[open] { background: var(--theme-popup-bg); padding: 0 0; overflow: hidden; + position: absolute; +} + +.settings-dropdown { + margin-left: 4px; +} + +.settings-icon::before { + /* Wheel <https://www.svgrepo.com/svg/384069/settings-cog-gear> */ + content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \ +enable-background="new 0 0 12 12" xmlns="http://www.w3.org/2000/svg">\ +<path d="M10.25,6c0-0.1243286-0.0261841-0.241333-0.0366211-0.362915l1.6077881-1.5545654l\ +-1.25-2.1650391 c0,0-1.2674561,0.3625488-2.1323853,0.6099854c-0.2034912-0.1431885-0.421875\ +-0.2639771-0.6494751-0.3701782L7.25,0h-2.5 c0,0-0.3214111,1.2857666-0.5393066,2.1572876\ +C3.9830933,2.2634888,3.7647095,2.3842773,3.5612183,2.5274658L1.428833,1.9174805 \ +l-1.25,2.1650391c0,0,0.9641113,0.9321899,1.6077881,1.5545654C1.7761841,5.758667,\ +1.75,5.8756714,1.75,6 s0.0261841,0.241333,0.0366211,0.362915L0.178833,7.9174805l1.25,\ +2.1650391l2.1323853-0.6099854 c0.2034912,0.1432495,0.421875,0.2639771,0.6494751,0.3701782\ +L4.75,12h2.5l0.5393066-2.1572876 c0.2276001-0.1062012,0.4459839-0.2269287,0.6494751\ +-0.3701782l2.1323853,0.6099854l1.25-2.1650391L10.2133789,6.362915 C10.2238159,6.241333,\ +10.25,6.1243286,10.25,6z M6,7.5C5.1715698,7.5,4.5,6.8284302,4.5,6S5.1715698,4.5,6,4.5S7.5\ +,5.1715698,7.5,6 S6.8284302,7.5,6,7.5z" fill="black"/></svg>'); + width: 18px; + height: 18px; + display: block; + filter: invert(0.7); + padding-left: 4px; + padding-top: 3px; +} + +.settings-choice { + padding: 4px; + width: 212px; +} + +.settings-choice label { + cursor: pointer; } -.theme-dropdown.open .theme-choice { +.theme-dropdown.open .theme-choice, .settings-dropdown.open .settings-choice { display: block; } From 8bf5a8332244dfc57cbd608ece30929ff44b7316 Mon Sep 17 00:00:00 2001 From: alexey semenyuk <alexsemenyuk88@gmail.com> Date: Tue, 30 Jul 2024 22:34:56 +0500 Subject: [PATCH 09/54] Fix example --- clippy_lints/src/unused_io_amount.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 448946bd66d51..cfc4ea46bdb2b 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -34,11 +34,18 @@ declare_clippy_lint! { /// ```rust,ignore /// use std::io; /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> { - /// // must be `w.write_all(b"foo")?;` /// w.write(b"foo")?; /// Ok(()) /// } /// ``` + /// Use instead: + /// ```rust,ignore + /// use std::io; + /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> { + /// w.write_all(b"foo")?; + /// Ok(()) + /// } + /// ``` #[clippy::version = "pre 1.29.0"] pub UNUSED_IO_AMOUNT, correctness, From 5364cbea80f0e9b251151f40aa67592b0827759e Mon Sep 17 00:00:00 2001 From: rzvxa <rzvxa@protonmail.com> Date: Sat, 3 Aug 2024 22:52:22 +0330 Subject: [PATCH 10/54] Respect allow inconsistent_struct_constructor on the type definition --- .../src/inconsistent_struct_constructor.rs | 4 ++++ .../ui/inconsistent_struct_constructor.fixed | 21 +++++++++++++++++++ tests/ui/inconsistent_struct_constructor.rs | 21 +++++++++++++++++++ .../ui/inconsistent_struct_constructor.stderr | 4 ++-- 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 5b0aadf35c62d..da289225509bf 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lint_allowed; use clippy_utils::source::snippet; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -71,6 +72,9 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { && let ty = cx.typeck_results().expr_ty(expr) && let Some(adt_def) = ty.ty_adt_def() && adt_def.is_struct() + && let Some(local_def_id) = adt_def.did().as_local() + && let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) + && !is_lint_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, ty_hir_id) && let Some(variant) = adt_def.variants().iter().next() { let mut def_order_map = FxHashMap::default(); diff --git a/tests/ui/inconsistent_struct_constructor.fixed b/tests/ui/inconsistent_struct_constructor.fixed index 5778f8f526f86..4c324587c96fa 100644 --- a/tests/ui/inconsistent_struct_constructor.fixed +++ b/tests/ui/inconsistent_struct_constructor.fixed @@ -15,6 +15,14 @@ struct Foo { z: i32, } +#[derive(Default)] +#[allow(clippy::inconsistent_struct_constructor)] +struct Bar { + x: i32, + y: i32, + z: i32, +} + mod without_base { use super::Foo; @@ -70,4 +78,17 @@ mod with_base { } } +mod with_allow_ty_def { + use super::Bar; + + fn test() { + let x = 1; + let y = 1; + let z = 1; + + // Should NOT lint because `Bar` is defined with `#[allow(clippy::inconsistent_struct_constructor)]` + Bar { y, x, z }; + } +} + fn main() {} diff --git a/tests/ui/inconsistent_struct_constructor.rs b/tests/ui/inconsistent_struct_constructor.rs index 9efaf0689342f..d49f236b9b07b 100644 --- a/tests/ui/inconsistent_struct_constructor.rs +++ b/tests/ui/inconsistent_struct_constructor.rs @@ -15,6 +15,14 @@ struct Foo { z: i32, } +#[derive(Default)] +#[allow(clippy::inconsistent_struct_constructor)] +struct Bar { + x: i32, + y: i32, + z: i32, +} + mod without_base { use super::Foo; @@ -74,4 +82,17 @@ mod with_base { } } +mod with_allow_ty_def { + use super::Bar; + + fn test() { + let x = 1; + let y = 1; + let z = 1; + + // Should NOT lint because `Bar` is defined with `#[allow(clippy::inconsistent_struct_constructor)]` + Bar { y, x, z }; + } +} + fn main() {} diff --git a/tests/ui/inconsistent_struct_constructor.stderr b/tests/ui/inconsistent_struct_constructor.stderr index 1192271f911fd..97bb7c789a720 100644 --- a/tests/ui/inconsistent_struct_constructor.stderr +++ b/tests/ui/inconsistent_struct_constructor.stderr @@ -1,5 +1,5 @@ error: struct constructor field order is inconsistent with struct definition field order - --> tests/ui/inconsistent_struct_constructor.rs:28:9 + --> tests/ui/inconsistent_struct_constructor.rs:36:9 | LL | Foo { y, x, z }; | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }` @@ -8,7 +8,7 @@ LL | Foo { y, x, z }; = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]` error: struct constructor field order is inconsistent with struct definition field order - --> tests/ui/inconsistent_struct_constructor.rs:55:9 + --> tests/ui/inconsistent_struct_constructor.rs:63:9 | LL | / Foo { LL | | z, From 234a1d35d9f1acc1da25b5069908ca2de209d322 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Mon, 5 Aug 2024 12:02:08 +0200 Subject: [PATCH 11/54] recognize metavariables in tail expressions --- clippy_lints/src/macro_metavars_in_unsafe.rs | 21 +++++++++++++++++-- .../macro_metavars_in_unsafe/default/test.rs | 15 ++++++++++++- .../default/test.stderr | 16 +++++++++++--- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index fed58f7ff1481..e215097142be9 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -122,8 +122,23 @@ struct BodyVisitor<'a, 'tcx> { /// within a relevant macro. macro_unsafe_blocks: Vec<HirId>, /// When this is >0, it means that the node currently being visited is "within" a - /// macro definition. This is not necessary for correctness, it merely helps reduce the number - /// of spans we need to insert into the map, since only spans from macros are relevant. + /// macro definition. + /// This is used to detect if an expression represents a metavariable. + /// + /// For example, the following pre-expansion code that we want to lint + /// ```ignore + /// macro_rules! m { ($e:expr) => { unsafe { $e; } } } + /// m!(1); + /// ``` + /// would look like this post-expansion code: + /// ```ignore + /// unsafe { /* macro */ + /// 1 /* root */; /* macro */ + /// } + /// ``` + /// Visiting the block and the statement will increment the `expn_depth` so that it is >0, + /// and visiting the expression with a root context while `expn_depth > 0` tells us + /// that it must be a metavariable. expn_depth: u32, cx: &'a LateContext<'tcx>, lint: &'a mut ExprMetavarsInUnsafe, @@ -157,7 +172,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> { && (self.lint.warn_unsafe_macro_metavars_in_private_macros || is_public_macro(self.cx, macro_def_id)) { self.macro_unsafe_blocks.push(block.hir_id); + self.expn_depth += 1; walk_block(self, block); + self.expn_depth -= 1; self.macro_unsafe_blocks.pop(); } else if ctxt.is_root() && self.expn_depth > 0 { let unsafe_block = self.macro_unsafe_blocks.last().copied(); diff --git a/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs b/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs index a312df5a43a07..3dafea56514db 100644 --- a/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs +++ b/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs @@ -1,7 +1,7 @@ //! Tests macro_metavars_in_unsafe with default configuration #![feature(decl_macro)] #![warn(clippy::macro_metavars_in_unsafe)] -#![allow(clippy::no_effect)] +#![allow(clippy::no_effect, clippy::not_unsafe_ptr_arg_deref)] #[macro_export] macro_rules! allow_works { @@ -237,6 +237,19 @@ macro_rules! nested_macros { }}; } +pub mod issue13219 { + #[macro_export] + macro_rules! m { + ($e:expr) => { + // Metavariable in a block tail expression + unsafe { $e } + }; + } + pub fn f(p: *const i32) -> i32 { + m!(*p) + } +} + fn main() { allow_works!(1); simple!(1); diff --git a/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr b/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr index d6b97f6fde1e1..6f0ebcbba0239 100644 --- a/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr +++ b/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr @@ -1,3 +1,15 @@ +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:245:13 + | +LL | unsafe { $e } + | ^^^^^^^^^^^^^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]` + error: this macro expands metavariables in an unsafe block --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:19:9 | @@ -10,8 +22,6 @@ LL | | } = note: this allows the user of the macro to write unsafe code outside of an unsafe block = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite - = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]` error: this macro expands metavariables in an unsafe block --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:30:9 @@ -183,5 +193,5 @@ LL | | } = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors From 5e25e7c37051df50009b7fad52adacd30eda5824 Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Wed, 7 Aug 2024 21:43:52 +0900 Subject: [PATCH 12/54] add a test for ice-3717.rs https://github.com/rust-lang/rust-clippy/issues/13099 --- tests/ui/crashes/ice-3717.fixed | 11 +++++++++++ tests/ui/crashes/ice-3717.rs | 2 -- tests/ui/crashes/ice-3717.stderr | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 tests/ui/crashes/ice-3717.fixed diff --git a/tests/ui/crashes/ice-3717.fixed b/tests/ui/crashes/ice-3717.fixed new file mode 100644 index 0000000000000..3f54b326979c9 --- /dev/null +++ b/tests/ui/crashes/ice-3717.fixed @@ -0,0 +1,11 @@ +#![deny(clippy::implicit_hasher)] + +use std::collections::HashSet; + +fn main() {} + +pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) { + //~^ ERROR: parameter of type `HashSet` should be generalized over different hashers + let _ = [0u8; 0]; + let _: HashSet<usize> = HashSet::default(); +} diff --git a/tests/ui/crashes/ice-3717.rs b/tests/ui/crashes/ice-3717.rs index 770f6cf448a63..2890a9277c719 100644 --- a/tests/ui/crashes/ice-3717.rs +++ b/tests/ui/crashes/ice-3717.rs @@ -1,7 +1,5 @@ #![deny(clippy::implicit_hasher)] -//@no-rustfix: need to change the suggestion to a multipart suggestion - use std::collections::HashSet; fn main() {} diff --git a/tests/ui/crashes/ice-3717.stderr b/tests/ui/crashes/ice-3717.stderr index 4b4618ee1bbd0..aac72c669654a 100644 --- a/tests/ui/crashes/ice-3717.stderr +++ b/tests/ui/crashes/ice-3717.stderr @@ -1,5 +1,5 @@ error: parameter of type `HashSet` should be generalized over different hashers - --> tests/ui/crashes/ice-3717.rs:9:21 + --> tests/ui/crashes/ice-3717.rs:7:21 | LL | pub fn ice_3717(_: &HashSet<usize>) { | ^^^^^^^^^^^^^^ From 6ce7e6d99f8bc97375f70331204c78f6438e7495 Mon Sep 17 00:00:00 2001 From: Michael Goulet <michael@errs.io> Date: Thu, 8 Aug 2024 11:56:12 -0400 Subject: [PATCH 13/54] Rename struct_tail_erasing_lifetimes to struct_tail_for_codegen --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index f206b2ceebcb1..553af913ef9df 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -142,7 +142,7 @@ fn check_rvalue<'tcx>( // We cannot allow this for now. return Err((span, "unsizing casts are only allowed for references right now".into())); }; - let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); + let unsized_ty = tcx.struct_tail_for_codegen(pointee_ty, tcx.param_env(def_id)); if let ty::Slice(_) | ty::Str = unsized_ty.kind() { check_operand(tcx, op, span, body, msrv)?; // Casting/coercing things to slices is fine. From 1ac76a2062b94b72c36650d21ac1af40b4aea0e3 Mon Sep 17 00:00:00 2001 From: Philipp Krones <hello@philkrones.com> Date: Thu, 8 Aug 2024 19:13:50 +0200 Subject: [PATCH 14/54] Merge commit 'cb806113e0f83a8f9b47d35b453b676543bcc40e' into clippy-subtree-update --- .cargo/config.toml | 7 + .github/workflows/clippy.yml | 13 +- .github/workflows/clippy_bors.yml | 17 +- .github/workflows/clippy_dev.yml | 5 +- .github/workflows/lintcheck.yml | 4 +- CHANGELOG.md | 2 + Cargo.toml | 3 +- book/src/lint_configuration.md | 6 +- clippy_config/Cargo.toml | 4 +- clippy_config/src/conf.rs | 724 +++++++++--------- clippy_config/src/lib.rs | 3 +- clippy_config/src/metadata.rs | 105 +-- clippy_dev/Cargo.toml | 3 - clippy_dev/src/fmt.rs | 392 +++++++--- clippy_dev/src/lib.rs | 1 - clippy_dev/src/main.rs | 5 +- clippy_dev/src/serve.rs | 15 +- clippy_dev/src/update_lints.rs | 324 +++----- clippy_lints/Cargo.toml | 1 - clippy_lints/src/approx_const.rs | 2 +- clippy_lints/src/as_conversions.rs | 10 +- clippy_lints/src/asm_syntax.rs | 22 +- clippy_lints/src/assertions_on_constants.rs | 4 +- .../src/assertions_on_result_states.rs | 47 +- clippy_lints/src/assigning_clones.rs | 29 +- clippy_lints/src/attrs/allow_attributes.rs | 20 +- .../attrs/allow_attributes_without_reason.rs | 10 +- clippy_lints/src/bool_to_int_with_if.rs | 4 +- clippy_lints/src/booleans.rs | 10 +- clippy_lints/src/casts/cast_lossless.rs | 8 +- clippy_lints/src/casts/cast_nan_to_int.rs | 4 +- .../src/casts/cast_possible_truncation.rs | 4 +- clippy_lints/src/casts/cast_sign_loss.rs | 6 +- .../src/casts/fn_to_numeric_cast_any.rs | 37 +- clippy_lints/src/casts/zero_ptr.rs | 4 +- clippy_lints/src/checked_conversions.rs | 4 +- clippy_lints/src/comparison_chain.rs | 4 +- clippy_lints/src/create_dir.rs | 24 +- clippy_lints/src/dbg_macro.rs | 108 +-- clippy_lints/src/declared_lints.rs | 3 +- clippy_lints/src/default.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 23 +- .../src/default_union_representation.rs | 16 +- clippy_lints/src/deprecated_lints.rs | 394 ++++------ clippy_lints/src/dereference.rs | 4 +- clippy_lints/src/doc/mod.rs | 9 +- clippy_lints/src/drop_forget_ref.rs | 18 +- clippy_lints/src/else_if_without_else.rs | 10 +- clippy_lints/src/empty_drop.rs | 19 +- clippy_lints/src/endian_bytes.rs | 89 +-- clippy_lints/src/enum_clike.rs | 2 +- clippy_lints/src/eta_reduction.rs | 327 ++++---- clippy_lints/src/exhaustive_items.rs | 6 +- .../src/field_scoped_visibility_modifiers.rs | 10 +- clippy_lints/src/float_literal.rs | 45 +- clippy_lints/src/floating_point_arithmetic.rs | 36 +- clippy_lints/src/format_push_string.rs | 10 +- clippy_lints/src/from_str_radix_10.rs | 6 +- clippy_lints/src/if_let_mutex.rs | 66 +- clippy_lints/src/if_not_else.rs | 4 +- clippy_lints/src/if_then_some_else_none.rs | 47 +- clippy_lints/src/implicit_hasher.rs | 53 +- clippy_lints/src/implicit_return.rs | 21 +- clippy_lints/src/implicit_saturating_add.rs | 8 +- clippy_lints/src/incompatible_msrv.rs | 7 +- clippy_lints/src/index_refutable_slice.rs | 4 +- clippy_lints/src/indexing_slicing.rs | 11 +- clippy_lints/src/infinite_iter.rs | 1 - clippy_lints/src/inherent_impl.rs | 9 +- .../src/invalid_upcast_comparisons.rs | 4 +- clippy_lints/src/large_include_file.rs | 16 +- clippy_lints/src/let_underscore.rs | 58 +- clippy_lints/src/lib.deprecated.rs | 78 -- clippy_lints/src/lib.rs | 81 +- clippy_lints/src/literal_representation.rs | 77 +- .../src/loops/explicit_counter_loop.rs | 8 +- clippy_lints/src/loops/for_kv_map.rs | 7 +- .../src/loops/manual_while_let_some.rs | 7 +- clippy_lints/src/loops/mod.rs | 10 +- clippy_lints/src/loops/needless_range_loop.rs | 11 +- .../src/loops/unused_enumerate_index.rs | 7 +- .../src/loops/while_immutable_condition.rs | 4 +- .../src/loops/while_let_on_iterator.rs | 7 +- clippy_lints/src/manual_clamp.rs | 13 +- clippy_lints/src/manual_float_methods.rs | 7 +- clippy_lints/src/manual_is_ascii_check.rs | 4 +- clippy_lints/src/manual_rem_euclid.rs | 8 +- clippy_lints/src/manual_rotate.rs | 4 +- .../src/manual_slice_size_calculation.rs | 4 +- clippy_lints/src/manual_strip.rs | 26 +- clippy_lints/src/manual_unwrap_or_default.rs | 4 +- clippy_lints/src/matches/manual_unwrap_or.rs | 4 +- clippy_lints/src/matches/match_ref_pats.rs | 8 +- clippy_lints/src/matches/match_wild_enum.rs | 39 +- .../src/matches/match_wild_err_arm.rs | 4 +- clippy_lints/src/matches/mod.rs | 6 +- clippy_lints/src/matches/overlapping_arms.rs | 30 +- clippy_lints/src/matches/redundant_guards.rs | 4 +- .../matches/rest_pat_in_fully_bound_struct.rs | 10 +- clippy_lints/src/matches/single_match.rs | 394 ++++++---- clippy_lints/src/matches/try_err.rs | 39 +- .../src/methods/bind_instead_of_map.rs | 19 +- clippy_lints/src/methods/clone_on_ref_ptr.rs | 23 +- clippy_lints/src/methods/filetype_is_file.rs | 9 +- clippy_lints/src/methods/get_unwrap.rs | 44 +- clippy_lints/src/methods/implicit_clone.rs | 6 +- .../src/methods/is_digit_ascii_radix.rs | 4 +- clippy_lints/src/methods/is_empty.rs | 4 +- clippy_lints/src/methods/iter_kv_map.rs | 15 +- clippy_lints/src/methods/iter_nth_zero.rs | 4 +- clippy_lints/src/methods/iter_skip_zero.rs | 4 +- .../src/methods/iterator_step_by_zero.rs | 4 +- clippy_lints/src/methods/map_clone.rs | 6 +- clippy_lints/src/methods/map_err_ignore.rs | 12 +- clippy_lints/src/methods/mod.rs | 28 +- clippy_lints/src/methods/open_options.rs | 2 +- clippy_lints/src/methods/repeat_once.rs | 4 +- clippy_lints/src/methods/str_splitn.rs | 4 +- .../src/methods/unnecessary_min_or_max.rs | 13 +- .../src/methods/unnecessary_to_owned.rs | 13 +- .../src/methods/unused_enumerate_index.rs | 7 +- clippy_lints/src/methods/useless_asref.rs | 6 +- .../src/methods/verbose_file_reads.rs | 7 +- .../src/methods/wrong_self_convention.rs | 2 +- clippy_lints/src/minmax.rs | 20 +- clippy_lints/src/misc_early/literal_suffix.rs | 30 +- .../src/misc_early/unneeded_field_pattern.rs | 21 +- clippy_lints/src/missing_assert_message.rs | 10 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/missing_trait_methods.rs | 46 +- .../src/mixed_read_write_in_expression.rs | 12 +- clippy_lints/src/module_style.rs | 32 +- .../src/needless_borrows_for_generic_args.rs | 48 +- clippy_lints/src/needless_pass_by_value.rs | 11 +- clippy_lints/src/no_effect.rs | 2 +- clippy_lints/src/non_copy_const.rs | 4 +- clippy_lints/src/only_used_in_recursion.rs | 1 - .../operators/absurd_extreme_comparisons.rs | 4 +- .../src/operators/arithmetic_side_effects.rs | 10 +- clippy_lints/src/operators/bit_mask.rs | 4 +- .../src/operators/const_comparisons.rs | 7 +- clippy_lints/src/operators/duration_subsec.rs | 4 +- clippy_lints/src/operators/erasing_op.rs | 6 +- clippy_lints/src/operators/float_cmp.rs | 8 +- clippy_lints/src/operators/identity_op.rs | 13 +- .../src/operators/integer_division.rs | 14 +- .../src/operators/modulo_arithmetic.rs | 16 +- .../src/operators/numeric_arithmetic.rs | 4 +- clippy_lints/src/operators/op_ref.rs | 6 +- clippy_lints/src/option_if_let_else.rs | 6 +- clippy_lints/src/partial_pub_fields.rs | 26 +- clippy_lints/src/pathbuf_init_then_push.rs | 4 +- clippy_lints/src/pattern_type_mismatch.rs | 30 +- clippy_lints/src/pub_use.rs | 14 +- clippy_lints/src/question_mark.rs | 8 +- clippy_lints/src/question_mark_used.rs | 10 +- clippy_lints/src/ranges.rs | 15 +- clippy_lints/src/redundant_clone.rs | 2 +- clippy_lints/src/redundant_slicing.rs | 68 +- clippy_lints/src/ref_patterns.rs | 14 +- clippy_lints/src/regex.rs | 4 +- clippy_lints/src/renamed_lints.rs | 65 -- clippy_lints/src/repeat_vec_with_capacity.rs | 4 +- clippy_lints/src/returns.rs | 2 +- clippy_lints/src/semicolon_block.rs | 12 +- clippy_lints/src/set_contains_or_insert.rs | 33 +- clippy_lints/src/shadow.rs | 13 +- .../src/single_char_lifetime_names.rs | 10 +- clippy_lints/src/size_of_ref.rs | 5 +- clippy_lints/src/std_instead_of_core.rs | 69 +- clippy_lints/src/strings.rs | 23 +- .../src/suspicious_xor_used_as_pow.rs | 15 +- clippy_lints/src/swap.rs | 4 +- clippy_lints/src/tests_outside_test_module.rs | 10 +- clippy_lints/src/transmute/mod.rs | 4 +- .../src/transmute/transmute_null_to_fn.rs | 7 +- .../src/transmute/transmuting_null.rs | 4 +- clippy_lints/src/types/borrowed_box.rs | 24 +- clippy_lints/src/types/rc_buffer.rs | 62 +- clippy_lints/src/types/rc_mutex.rs | 14 +- .../src/undocumented_unsafe_blocks.rs | 53 +- clippy_lints/src/unicode.rs | 64 +- clippy_lints/src/unused_result_ok.rs | 59 ++ .../interning_defined_symbol.rs | 6 +- .../src/utils/internal_lints/invalid_paths.rs | 8 +- .../internal_lints/lint_without_lint_pass.rs | 85 +- .../internal_lints/metadata_collector.rs | 117 ++- clippy_lints/src/vec.rs | 4 +- clippy_lints/src/visibility.rs | 43 +- clippy_lints/src/zero_div_zero.rs | 7 +- clippy_utils/Cargo.toml | 3 - clippy_utils/src/consts.rs | 279 ++++--- clippy_utils/src/diagnostics.rs | 29 - clippy_utils/src/eager_or_lazy.rs | 16 +- clippy_utils/src/higher.rs | 13 +- clippy_utils/src/hir_utils.rs | 12 +- clippy_utils/src/lib.rs | 46 +- clippy_utils/src/paths.rs | 1 - clippy_utils/src/source.rs | 129 ++-- clippy_utils/src/ty.rs | 13 - clippy_utils/src/visitors.rs | 98 ++- declare_clippy_lint/Cargo.toml | 3 - declare_clippy_lint/src/lib.rs | 1 - lintcheck/Cargo.toml | 3 - lintcheck/src/config.rs | 8 +- lintcheck/src/input.rs | 6 +- lintcheck/src/main.rs | 28 +- rust-toolchain | 2 +- rustc_tools_util/Cargo.toml | 3 - rustc_tools_util/src/lib.rs | 59 +- src/driver.rs | 2 - src/main.rs | 1 - tests/check-fmt.rs | 1 - tests/compile-test.rs | 6 +- tests/dogfood.rs | 1 - tests/integration.rs | 1 - tests/lint_message_convention.rs | 1 - tests/missing-test-files.rs | 1 - .../ui-internal/default_deprecation_reason.rs | 30 - .../default_deprecation_reason.stderr | 22 - .../excessive_nesting/excessive_nesting.rs | 20 +- .../excessive_nesting.stderr | 74 +- tests/ui-toml/unwrap_used/unwrap_used.stderr | 134 +++- tests/ui/assigning_clones.fixed | 68 ++ tests/ui/assigning_clones.rs | 68 ++ tests/ui/assigning_clones.stderr | 86 ++- tests/ui/bind_instead_of_map_multipart.stderr | 10 +- tests/ui/crashes/ice-3717.stderr | 11 +- tests/ui/crashes/ice-6254.rs | 3 +- tests/ui/create_dir.stderr | 13 +- tests/ui/dbg_macro/dbg_macro.stderr | 4 +- tests/ui/dbg_macro/dbg_macro_unfixable.stderr | 2 +- tests/ui/deprecated.rs | 31 +- tests/ui/deprecated.stderr | 68 +- tests/ui/deprecated_old.rs | 9 - tests/ui/deprecated_old.stderr | 23 - tests/ui/deref_by_slicing.fixed | 4 + tests/ui/deref_by_slicing.rs | 4 + tests/ui/deref_by_slicing.stderr | 8 +- tests/ui/doc/footnote_issue_13183.rs | 10 + tests/ui/empty_drop.stderr | 7 +- tests/ui/eta.fixed | 17 + tests/ui/eta.rs | 17 + tests/ui/eta.stderr | 74 +- tests/ui/excessive_precision.stderr | 111 ++- tests/ui/explicit_counter_loop.rs | 13 + tests/ui/explicit_counter_loop.stderr | 8 +- tests/ui/fn_to_numeric_cast_any.stderr | 118 ++- tests/ui/for_kv_map.fixed | 10 + tests/ui/for_kv_map.rs | 10 + tests/ui/for_kv_map.stderr | 13 +- tests/ui/get_unwrap.stderr | 152 +++- tests/ui/if_let_mutex.rs | 9 + tests/ui/if_let_mutex.stderr | 24 +- tests/ui/if_then_some_else_none.fixed | 119 +++ tests/ui/if_then_some_else_none.rs | 2 +- tests/ui/if_then_some_else_none.stderr | 19 +- tests/ui/implicit_hasher.fixed | 102 +++ tests/ui/implicit_hasher.rs | 9 +- tests/ui/implicit_hasher.stderr | 131 +++- tests/ui/implicit_return.stderr | 104 ++- tests/ui/lossy_float_literal.stderr | 76 +- tests/ui/missing_trait_methods.rs | 8 + tests/ui/missing_trait_methods.stderr | 38 +- .../needless_borrows_for_generic_args.fixed | 8 + tests/ui/needless_borrows_for_generic_args.rs | 8 + tests/ui/needless_pub_self.stderr | 11 +- tests/ui/nonminimal_bool.rs | 6 + tests/ui/patterns.fixed | 2 +- tests/ui/patterns.rs | 2 +- tests/ui/rename.fixed | 131 ++-- tests/ui/rename.rs | 131 ++-- tests/ui/rename.stderr | 158 ++-- tests/ui/set_contains_or_insert.rs | 83 +- tests/ui/set_contains_or_insert.stderr | 72 +- tests/ui/single_match.fixed | 43 ++ tests/ui/single_match.rs | 49 ++ tests/ui/single_match.stderr | 20 +- tests/ui/std_instead_of_core.fixed | 17 +- tests/ui/std_instead_of_core.rs | 17 +- tests/ui/std_instead_of_core.stderr | 14 +- tests/ui/suspicious_xor_used_as_pow.stderr | 47 +- tests/ui/try_err.fixed | 10 +- tests/ui/try_err.rs | 10 +- tests/ui/uninit_vec.rs | 2 +- tests/ui/unneeded_field_pattern.rs | 2 +- tests/ui/unused_result_ok.fixed | 40 + tests/ui/unused_result_ok.rs | 40 + tests/ui/unused_result_ok.stderr | 52 ++ tests/ui/unwrap_expect_used.rs | 2 +- tests/ui/while_let_on_iterator.fixed | 9 + tests/ui/while_let_on_iterator.rs | 9 + tests/ui/while_let_on_iterator.stderr | 8 +- tests/versioncheck.rs | 1 - tests/workspace_test/path_dep/Cargo.toml | 3 + util/gh-pages/index.html | 4 +- util/gh-pages/script.js | 74 +- 297 files changed, 5572 insertions(+), 4012 deletions(-) delete mode 100644 clippy_lints/src/lib.deprecated.rs delete mode 100644 clippy_lints/src/renamed_lints.rs create mode 100644 clippy_lints/src/unused_result_ok.rs delete mode 100644 tests/ui-internal/default_deprecation_reason.rs delete mode 100644 tests/ui-internal/default_deprecation_reason.stderr delete mode 100644 tests/ui/deprecated_old.rs delete mode 100644 tests/ui/deprecated_old.stderr create mode 100644 tests/ui/doc/footnote_issue_13183.rs create mode 100644 tests/ui/if_then_some_else_none.fixed create mode 100644 tests/ui/implicit_hasher.fixed create mode 100644 tests/ui/unused_result_ok.fixed create mode 100644 tests/ui/unused_result_ok.rs create mode 100644 tests/ui/unused_result_ok.stderr diff --git a/.cargo/config.toml b/.cargo/config.toml index 7afdd068a9905..ce07290d1e1e5 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -13,6 +13,13 @@ target-dir = "target" [unstable] binary-dep-depinfo = true +profile-rustflags = true [profile.dev] split-debuginfo = "unpacked" + +# Add back the containing directory of the packages we have to refer to using --manifest-path +[profile.dev.package.clippy_dev] +rustflags = ["--remap-path-prefix", "=clippy_dev"] +[profile.dev.package.lintcheck] +rustflags = ["--remap-path-prefix", "=lintcheck"] diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 06bf3b6fdbfab..0a0538490cce9 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 + RUSTFLAGS: -D warnings concurrency: # For a given workflow, if we push to the same PR, cancel all previous builds on that PR. @@ -47,25 +48,25 @@ jobs: # Run - name: Build - run: cargo build --tests --features deny-warnings,internal + run: cargo build --tests --features internal - name: Test - run: cargo test --features deny-warnings,internal + run: cargo test --features internal - name: Test clippy_lints - run: cargo test --features deny-warnings,internal + run: cargo test --features internal working-directory: clippy_lints - name: Test clippy_utils - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_utils - name: Test rustc_tools_util - run: cargo test --features deny-warnings + run: cargo test working-directory: rustc_tools_util - name: Test clippy_dev - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_dev - name: Test clippy-driver diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 1f4bec9291823..10e18e84c89fd 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 + RUSTFLAGS: -D warnings concurrency: # For a given workflow, if we push to the same branch, cancel all previous builds on that branch. @@ -85,34 +86,34 @@ jobs: # Run - name: Build - run: cargo build --tests --features deny-warnings,internal + run: cargo build --tests --features internal - name: Test if: matrix.host == 'x86_64-unknown-linux-gnu' - run: cargo test --features deny-warnings,internal + run: cargo test --features internal - name: Test if: matrix.host != 'x86_64-unknown-linux-gnu' - run: cargo test --features deny-warnings,internal -- --skip dogfood + run: cargo test --features internal -- --skip dogfood - name: Test clippy_lints - run: cargo test --features deny-warnings,internal + run: cargo test --features internal working-directory: clippy_lints - name: Test clippy_utils - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_utils - name: Test clippy_config - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_config - name: Test rustc_tools_util - run: cargo test --features deny-warnings + run: cargo test working-directory: rustc_tools_util - name: Test clippy_dev - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_dev - name: Test clippy-driver diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 37f18a4c08740..cf0a8bde202ad 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -16,6 +16,7 @@ on: env: RUST_BACKTRACE: 1 CARGO_INCREMENTAL: 0 + RUSTFLAGS: -D warnings jobs: clippy_dev: @@ -28,7 +29,7 @@ jobs: # Run - name: Build - run: cargo build --features deny-warnings + run: cargo build working-directory: clippy_dev - name: Test update_lints @@ -38,6 +39,8 @@ jobs: run: cargo dev fmt --check - name: Test cargo dev new lint + env: + RUSTFLAGS: -A unused-imports run: | cargo dev new_lint --name new_early_pass --pass early cargo dev new_lint --name new_late_pass --pass late diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index 6a5139b6dc0bb..3cbda0b382436 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -58,7 +58,7 @@ jobs: - name: Run lintcheck if: steps.cache-json.outputs.cache-hit != 'true' - run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml + run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload base JSON uses: actions/upload-artifact@v4 @@ -86,7 +86,7 @@ jobs: run: cargo build --manifest-path=lintcheck/Cargo.toml - name: Run lintcheck - run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml + run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload head JSON uses: actions/upload-artifact@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 60c03b03d9be3..fddc2fd994e87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5830,6 +5830,7 @@ Released 2018-09-13 [`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use +[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push @@ -5998,6 +5999,7 @@ Released 2018-09-13 [`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 +[`unused_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_result_ok [`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit diff --git a/Cargo.toml b/Cargo.toml index bb4dc97e748e3..78409c7a09e29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ color-print = "0.3.4" anstream = "0.6.0" [dev-dependencies] -ui_test = "0.24" +ui_test = "0.25" regex = "1.5.5" toml = "0.7.3" walkdir = "2.3" @@ -51,7 +51,6 @@ tokio = { version = "1", features = ["io-util"] } rustc_tools_util = "0.3.0" [features] -deny-warnings = ["clippy_lints/deny-warnings"] integration = ["tempfile"] internal = ["clippy_lints/internal", "tempfile"] diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index fb717a2c166d4..e3d550b146629 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -364,7 +364,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat ## `await-holding-invalid-types` - +The list of types which may not be held across an await point. **Default Value:** `[]` @@ -668,6 +668,8 @@ crate. For example, `pub(crate)` items. ## `msrv` The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` +**Default Value:** `current version` + --- **Affected lints:** * [`allow_attributes`](https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes) @@ -862,6 +864,8 @@ The maximum number of lines a function or method can have The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. By default there is no limit +**Default Value:** `target_pointer_width * 2` + --- **Affected lints:** * [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index e1b2edc8a6ff9..d5b28e2532371 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +itertools = "0.12" rustc-semver = "1.1" serde = { version = "1.0", features = ["derive"] } toml = "0.7.3" @@ -13,9 +14,6 @@ toml = "0.7.3" [dev-dependencies] walkdir = "2.3" -[features] -deny-warnings = [] - [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] rustc_private = true diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 63140a36875da..4c2a8255d6b65 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -123,7 +123,8 @@ macro_rules! define_Conf { $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])? $(#[default_text = $default_text:expr])? - ($name:ident: $ty:ty = $default:expr), + $(#[lints($($for_lints:ident),* $(,)?)])? + $name:ident: $ty:ty = $default:expr, )*) => { /// Clippy lint configuration pub struct Conf { @@ -201,29 +202,128 @@ macro_rules! define_Conf { } pub fn get_configuration_metadata() -> Vec<ClippyConfiguration> { - let mut sorted = vec![ - $( - { - let deprecation_reason = wrap_option!($($dep)?); - - ClippyConfiguration::new( - stringify!($name), - default_text!(defaults::$name() $(, $default_text)?), - concat!($($doc, '\n',)*), - deprecation_reason, - ) - }, - )+ - ]; - sorted.sort_by(|a, b| a.name.cmp(&b.name)); - sorted + vec![$( + ClippyConfiguration { + name: stringify!($name).replace('_', "-"), + default: default_text!(defaults::$name() $(, $default_text)?), + lints: &[$($(stringify!($for_lints)),*)?], + doc: concat!($($doc, '\n',)*), + deprecation_reason: wrap_option!($($dep)?) + }, + )*] } }; } define_Conf! { - /// Lint: ARITHMETIC_SIDE_EFFECTS. + /// Which crates to allow absolute paths from + #[lints(absolute_paths)] + absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default(), + /// The maximum number of segments a path can have before being linted, anything above this will + /// be linted. + #[lints(absolute_paths)] + absolute_paths_max_segments: u64 = 2, + /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block + #[lints(undocumented_unsafe_blocks)] + accept_comment_above_attributes: bool = true, + /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block + #[lints(undocumented_unsafe_blocks)] + accept_comment_above_statement: bool = true, + /// Don't lint when comparing the result of a modulo operation to zero. + #[lints(modulo_arithmetic)] + allow_comparison_to_zero: bool = true, + /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` + #[lints(dbg_macro)] + allow_dbg_in_tests: bool = false, + /// Whether `expect` should be allowed in test functions or `#[cfg(test)]` + #[lints(expect_used)] + allow_expect_in_tests: bool = false, + /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` + #[lints(uninlined_format_args)] + allow_mixed_uninlined_format_args: bool = true, + /// Whether to allow `r#""#` when `r""` can be used + #[lints(unnecessary_raw_string_hashes)] + allow_one_hash_in_raw_strings: bool = false, + /// Whether `panic` should be allowed in test functions or `#[cfg(test)]` + #[lints(panic)] + allow_panic_in_tests: bool = false, + /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` + #[lints(print_stderr, print_stdout)] + allow_print_in_tests: bool = false, + /// Whether to allow module inception if it's not public. + #[lints(module_inception)] + allow_private_module_inception: bool = false, + /// List of trait paths to ignore when checking renamed function parameters. + /// + /// #### Example + /// + /// ```toml + /// allow-renamed-params-for = [ "std::convert::From" ] + /// ``` + /// + /// #### Noteworthy + /// + /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr` + /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the + /// default configuration of Clippy. By default, any configuration will replace the default value. + #[lints(renamed_function_params)] + allow_renamed_params_for: Vec<String> = + DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect(), + /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` + #[lints(unwrap_used)] + allow_unwrap_in_tests: bool = false, + /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]` + #[lints(useless_vec)] + allow_useless_vec_in_tests: bool = false, + /// Additional dotfiles (files or directories starting with a dot) to allow + #[lints(path_ends_with_ext)] + allowed_dotfiles: Vec<String> = Vec::default(), + /// A list of crate names to allow duplicates of + #[lints(multiple_crate_versions)] + allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default(), + /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of + /// the list to indicate, that the configured values should be appended to the default + /// configuration of Clippy. By default, any configuration will replace the default value. + #[lints(min_ident_chars)] + allowed_idents_below_min_chars: FxHashSet<String> = + DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect(), + /// List of prefixes to allow when determining whether an item's name ends with the module's name. + /// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`), + /// then don't emit a warning. + /// + /// #### Example + /// + /// ```toml + /// allowed-prefixes = [ "to", "from" ] + /// ``` + /// + /// #### Noteworthy + /// + /// - By default, the following prefixes are allowed: `to`, `as`, `into`, `from`, `try_into` and `try_from` + /// - PascalCase variant is included automatically for each snake_case variant (e.g. if `try_into` is included, + /// `TryInto` will also be included) + /// - Use `".."` as part of the list to indicate that the configured values should be appended to the + /// default configuration of Clippy. By default, any configuration will replace the default value + #[lints(module_name_repetitions)] + allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect(), + /// The list of unicode scripts allowed to be used in the scope. + #[lints(disallowed_script_idents)] + allowed_scripts: Vec<String> = vec!["Latin".to_string()], + /// List of path segments allowed to have wildcard imports. /// + /// #### Example + /// + /// ```toml + /// allowed-wildcard-imports = [ "utils", "common" ] + /// ``` + /// + /// #### Noteworthy + /// + /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`. + /// 2. Paths with any segment that containing the word 'prelude' + /// are already allowed by default. + #[lints(wildcard_imports)] + allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default(), /// Suppress checking of the passed type names in all types of operations. /// /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. @@ -238,9 +338,8 @@ define_Conf! { /// /// A type, say `SomeType`, listed in this configuration has the same behavior of /// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. - (arithmetic_side_effects_allowed: Vec<String> = <_>::default()), - /// Lint: ARITHMETIC_SIDE_EFFECTS. - /// + #[lints(arithmetic_side_effects)] + arithmetic_side_effects_allowed: Vec<String> = <_>::default(), /// Suppress checking of the passed type pair names in binary operations like addition or /// multiplication. /// @@ -255,9 +354,8 @@ define_Conf! { /// ```toml /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] /// ``` - (arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default()), - /// Lint: ARITHMETIC_SIDE_EFFECTS. - /// + #[lints(arithmetic_side_effects)] + arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default(), /// Suppress checking of the passed type names in unary operations like "negation" (`-`). /// /// #### Example @@ -265,298 +363,78 @@ define_Conf! { /// ```toml /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] /// ``` - (arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default()), - /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS, SINGLE_CALL_FN, NEEDLESS_PASS_BY_REF_MUT. - /// + #[lints(arithmetic_side_effects)] + arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default(), + /// The maximum allowed size for arrays on the stack + #[lints(large_const_arrays, large_stack_arrays)] + array_size_threshold: u64 = 512_000, /// 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, OPTION_MAP_UNWRAP_OR, 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, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS, MANUAL_PATTERN_CHAR_COMPARISON, ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON, COLLAPSIBLE_MATCH. - /// - /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` - #[default_text = ""] - (msrv: Msrv = Msrv::empty()), + #[lints( + box_collection, + enum_variant_names, + large_types_passed_by_value, + linkedlist, + needless_pass_by_ref_mut, + option_option, + rc_buffer, + rc_mutex, + redundant_allocation, + single_call_fn, + trivially_copy_pass_by_ref, + unnecessary_box_returns, + unnecessary_wraps, + unused_self, + upper_case_acronyms, + vec_box, + wrong_self_convention, + )] + avoid_breaking_exported_api: bool = true, + /// The list of types which may not be held across an await point. + #[lints(await_holding_invalid_type)] + await_holding_invalid_types: Vec<DisallowedPath> = Vec::new(), /// DEPRECATED LINT: BLACKLISTED_NAME. /// /// Use the Disallowed Names lint instead #[conf_deprecated("Please use `disallowed-names` instead", disallowed_names)] - (blacklisted_names: Vec<String> = Vec::new()), - /// Lint: COGNITIVE_COMPLEXITY. - /// + blacklisted_names: Vec<String> = Vec::new(), + /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. + #[lints(cargo_common_metadata)] + cargo_ignore_publish: bool = false, + /// Whether to also run the listed lints on private items. + #[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)] + check_private_items: bool = false, /// The maximum cognitive complexity a function can have - (cognitive_complexity_threshold: u64 = 25), - /// Lint: EXCESSIVE_NESTING. - /// - /// The maximum amount of nesting a block can reside in - (excessive_nesting_threshold: u64 = 0), + #[lints(cognitive_complexity)] + cognitive_complexity_threshold: u64 = 25, /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. /// /// Use the Cognitive Complexity lint instead. #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)] - (cyclomatic_complexity_threshold: u64 = 25), - /// Lint: DISALLOWED_NAMES. - /// + cyclomatic_complexity_threshold: u64 = 25, + /// The list of disallowed macros, written as fully qualified paths. + #[lints(disallowed_macros)] + disallowed_macros: Vec<DisallowedPath> = Vec::new(), + /// The list of disallowed methods, written as fully qualified paths. + #[lints(disallowed_methods)] + disallowed_methods: Vec<DisallowedPath> = Vec::new(), /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value /// `".."` can be used as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. - (disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()), - /// Lint: SEMICOLON_INSIDE_BLOCK. - /// - /// Whether to lint only if it's multiline. - (semicolon_inside_block_ignore_singleline: bool = false), - /// Lint: SEMICOLON_OUTSIDE_BLOCK. - /// - /// Whether to lint only if it's singleline. - (semicolon_outside_block_ignore_multiline: bool = false), - /// Lint: DOC_MARKDOWN. - /// + #[lints(disallowed_names)] + disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(), + /// The list of disallowed types, written as fully qualified paths. + #[lints(disallowed_types)] + disallowed_types: Vec<DisallowedPath> = Vec::new(), /// The list of words this lint should not consider as identifiers needing ticks. The value /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. For example: /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. - (doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()), - /// Lint: TOO_MANY_ARGUMENTS. - /// - /// The maximum number of argument a function or method can have - (too_many_arguments_threshold: u64 = 7), - /// Lint: TYPE_COMPLEXITY. - /// - /// The maximum complexity a type can have - (type_complexity_threshold: u64 = 250), - /// Lint: MANY_SINGLE_CHAR_NAMES. - /// - /// The maximum number of single char bindings a scope may have - (single_char_binding_names_threshold: u64 = 4), - /// Lint: BOXED_LOCAL, USELESS_VEC. - /// - /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap - (too_large_for_stack: u64 = 200), - /// Lint: ENUM_VARIANT_NAMES. - /// - /// The minimum number of enum variants for the lints about variant names to trigger - (enum_variant_name_threshold: u64 = 3), - /// Lint: STRUCT_FIELD_NAMES. - /// - /// The minimum number of struct fields for the lints about field names to trigger - (struct_field_name_threshold: u64 = 3), - /// Lint: LARGE_ENUM_VARIANT. - /// - /// The maximum size of an enum's variant to avoid box suggestion - (enum_variant_size_threshold: u64 = 200), - /// Lint: VERBOSE_BIT_MASK. - /// - /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' - (verbose_bit_mask_threshold: u64 = 1), - /// Lint: DECIMAL_LITERAL_REPRESENTATION. - /// - /// The lower bound for linting decimal literals - (literal_representation_threshold: u64 = 16384), - /// Lint: TRIVIALLY_COPY_PASS_BY_REF. - /// - /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by - /// reference. By default there is no limit - #[default_text = ""] - (trivial_copy_size_limit: Option<u64> = None), - /// Lint: LARGE_TYPES_PASSED_BY_VALUE. - /// - /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. - (pass_by_value_size_limit: u64 = 256), - /// Lint: TOO_MANY_LINES. - /// - /// The maximum number of lines a function or method can have - (too_many_lines_threshold: u64 = 100), - /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. - /// - /// The maximum allowed size for arrays on the stack - (array_size_threshold: u64 = 512_000), - /// Lint: LARGE_STACK_FRAMES. - /// - /// The maximum allowed stack size for functions in bytes - (stack_size_threshold: u64 = 512_000), - /// Lint: VEC_BOX. - /// - /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed - (vec_box_size_threshold: u64 = 4096), - /// Lint: TYPE_REPETITION_IN_BOUNDS. - /// - /// The maximum number of bounds a trait can have to be linted - (max_trait_bounds: u64 = 3), - /// Lint: STRUCT_EXCESSIVE_BOOLS. - /// - /// The maximum number of bool fields a struct can have - (max_struct_bools: u64 = 3), - /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. - /// - /// The maximum number of bool parameters a function can have - (max_fn_params_bools: u64 = 3), - /// Lint: WILDCARD_IMPORTS. - /// - /// Whether to allow certain wildcard imports (prelude, super in tests). - (warn_on_all_wildcard_imports: bool = false), - /// Lint: DISALLOWED_MACROS. - /// - /// The list of disallowed macros, written as fully qualified paths. - (disallowed_macros: Vec<DisallowedPath> = Vec::new()), - /// Lint: DISALLOWED_METHODS. - /// - /// The list of disallowed methods, written as fully qualified paths. - (disallowed_methods: Vec<DisallowedPath> = Vec::new()), - /// Lint: DISALLOWED_TYPES. - /// - /// The list of disallowed types, written as fully qualified paths. - (disallowed_types: Vec<DisallowedPath> = Vec::new()), - /// Lint: UNREADABLE_LITERAL. - /// - /// Should the fraction of a decimal be linted to include separators. - (unreadable_literal_lint_fractions: bool = true), - /// Lint: UPPER_CASE_ACRONYMS. - /// - /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other - (upper_case_acronyms_aggressive: bool = false), - /// Lint: MANUAL_LET_ELSE. - /// - /// Whether the matches should be considered by the lint, and whether there should - /// be filtering for common types. - (matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes), - /// Lint: CARGO_COMMON_METADATA. - /// - /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. - (cargo_ignore_publish: bool = false), - /// Lint: NONSTANDARD_MACRO_BRACES. - /// - /// Enforce the named macros always use the braces specified. - /// - /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro - /// could be used with a full path two `MacroMatcher`s have to be added one with the full path - /// `crate_name::macro_name` and one with just the macro name. - (standard_macro_braces: Vec<MacroMatcher> = Vec::new()), - /// Lint: MISSING_ENFORCED_IMPORT_RENAMES. - /// - /// The list of imports to always rename, a fully qualified path followed by the rename. - (enforced_import_renames: Vec<Rename> = Vec::new()), - /// Lint: DISALLOWED_SCRIPT_IDENTS. - /// - /// The list of unicode scripts allowed to be used in the scope. - (allowed_scripts: Vec<String> = vec!["Latin".to_string()]), - /// Lint: NON_SEND_FIELDS_IN_SEND_TY. - /// + #[lints(doc_markdown)] + doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(), /// Whether to apply the raw pointer heuristic to determine if a type is `Send`. - (enable_raw_pointer_heuristic_for_send: bool = true), - /// Lint: INDEX_REFUTABLE_SLICE. - /// - /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in - /// the slice pattern that is suggested. If more elements are necessary, the lint is suppressed. - /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. - (max_suggested_slice_pattern_length: u64 = 3), - /// Lint: AWAIT_HOLDING_INVALID_TYPE. - (await_holding_invalid_types: Vec<DisallowedPath> = Vec::new()), - /// Lint: LARGE_INCLUDE_FILE. - /// - /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes - (max_include_file_size: u64 = 1_000_000), - /// Lint: EXPECT_USED. - /// - /// Whether `expect` should be allowed in test functions or `#[cfg(test)]` - (allow_expect_in_tests: bool = false), - /// Lint: UNWRAP_USED. - /// - /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` - (allow_unwrap_in_tests: bool = false), - /// Lint: PANIC. - /// - /// Whether `panic` should be allowed in test functions or `#[cfg(test)]` - (allow_panic_in_tests: bool = false), - /// Lint: DBG_MACRO. - /// - /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` - (allow_dbg_in_tests: bool = false), - /// Lint: PRINT_STDOUT, PRINT_STDERR. - /// - /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` - (allow_print_in_tests: bool = false), - /// Lint: USELESS_VEC. - /// - /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]` - (allow_useless_vec_in_tests: bool = false), - /// Lint: RESULT_LARGE_ERR. - /// - /// The maximum size of the `Err`-variant in a `Result` returned from a function - (large_error_threshold: u64 = 128), - /// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND, BORROW_INTERIOR_MUTABLE_CONST, DECLARE_INTERIOR_MUTABLE_CONST. - /// - /// A list of paths to types that should be treated as if they do not contain interior mutability - (ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])), - /// Lint: UNINLINED_FORMAT_ARGS. - /// - /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` - (allow_mixed_uninlined_format_args: bool = true), - /// Lint: INDEXING_SLICING. - /// - /// Whether to suppress a restriction lint in constant code. In same - /// cases the restructured operation might not be unavoidable, as the - /// suggested counterparts are unavailable in constant code. This - /// configuration will cause restriction lints to trigger even - /// if no suggestion can be made. - (suppress_restriction_lint_in_const: bool = false), - /// Lint: MISSING_DOCS_IN_PRIVATE_ITEMS. - /// - /// Whether to **only** check for missing documentation in items visible within the current - /// crate. For example, `pub(crate)` items. - (missing_docs_in_crate_items: bool = false), - /// Lint: LARGE_FUTURES. - /// - /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint - (future_size_threshold: u64 = 16 * 1024), - /// Lint: UNNECESSARY_BOX_RETURNS. - /// - /// The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint - (unnecessary_box_size: u64 = 128), - /// Lint: MODULE_INCEPTION. - /// - /// Whether to allow module inception if it's not public. - (allow_private_module_inception: bool = false), - /// Lint: MIN_IDENT_CHARS. - /// - /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of - /// the list to indicate, that the configured values should be appended to the default - /// configuration of Clippy. By default, any configuration will replace the default value. - (allowed_idents_below_min_chars: FxHashSet<String> = - DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect()), - /// Lint: MIN_IDENT_CHARS. - /// - /// Minimum chars an ident can have, anything below or equal to this will be linted. - (min_ident_chars_threshold: u64 = 1), - /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS. - /// - /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block - (accept_comment_above_statement: bool = true), - /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS. - /// - /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block - (accept_comment_above_attributes: bool = true), - /// Lint: UNNECESSARY_RAW_STRING_HASHES. - /// - /// Whether to allow `r#""#` when `r""` can be used - (allow_one_hash_in_raw_strings: bool = false), - /// Lint: ABSOLUTE_PATHS. - /// - /// The maximum number of segments a path can have before being linted, anything above this will - /// be linted. - (absolute_paths_max_segments: u64 = 2), - /// Lint: ABSOLUTE_PATHS. - /// - /// Which crates to allow absolute paths from - (absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default()), - /// Lint: PATH_ENDS_WITH_EXT. - /// - /// Additional dotfiles (files or directories starting with a dot) to allow - (allowed_dotfiles: Vec<String> = Vec::default()), - /// Lint: MULTIPLE_CRATE_VERSIONS. - /// - /// A list of crate names to allow duplicates of - (allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default()), - /// Lint: EXPLICIT_ITER_LOOP. - /// + #[lints(non_send_fields_in_send_ty)] + enable_raw_pointer_heuristic_for_send: bool = true, /// Whether to recommend using implicit into iter for reborrowed values. /// /// #### Example @@ -574,77 +452,195 @@ define_Conf! { /// for _ in &*rmvec {} /// for _ in &mut *rmvec {} /// ``` - (enforce_iter_loop_reborrow: bool = false), - /// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC. - /// - /// Whether to also run the listed lints on private items. - (check_private_items: bool = false), - /// Lint: PUB_UNDERSCORE_FIELDS. - /// + #[lints(explicit_iter_loop)] + enforce_iter_loop_reborrow: bool = false, + /// The list of imports to always rename, a fully qualified path followed by the rename. + #[lints(missing_enforced_import_renames)] + enforced_import_renames: Vec<Rename> = Vec::new(), + /// The minimum number of enum variants for the lints about variant names to trigger + #[lints(enum_variant_names)] + enum_variant_name_threshold: u64 = 3, + /// The maximum size of an enum's variant to avoid box suggestion + #[lints(large_enum_variant)] + enum_variant_size_threshold: u64 = 200, + /// The maximum amount of nesting a block can reside in + #[lints(excessive_nesting)] + excessive_nesting_threshold: u64 = 0, + /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint + #[lints(large_futures)] + future_size_threshold: u64 = 16 * 1024, + /// A list of paths to types that should be treated as if they do not contain interior mutability + #[lints(borrow_interior_mutable_const, declare_interior_mutable_const, ifs_same_cond, mutable_key_type)] + ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()]), + /// The maximum size of the `Err`-variant in a `Result` returned from a function + #[lints(result_large_err)] + large_error_threshold: u64 = 128, + /// The lower bound for linting decimal literals + #[lints(decimal_literal_representation)] + literal_representation_threshold: u64 = 16384, + /// Whether the matches should be considered by the lint, and whether there should + /// be filtering for common types. + #[lints(manual_let_else)] + matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes, + /// The maximum number of bool parameters a function can have + #[lints(fn_params_excessive_bools)] + max_fn_params_bools: u64 = 3, + /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes + #[lints(large_include_file)] + max_include_file_size: u64 = 1_000_000, + /// The maximum number of bool fields a struct can have + #[lints(struct_excessive_bools)] + max_struct_bools: u64 = 3, + /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in + /// the slice pattern that is suggested. If more elements are necessary, the lint is suppressed. + /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. + #[lints(index_refutable_slice)] + max_suggested_slice_pattern_length: u64 = 3, + /// The maximum number of bounds a trait can have to be linted + #[lints(type_repetition_in_bounds)] + max_trait_bounds: u64 = 3, + /// Minimum chars an ident can have, anything below or equal to this will be linted. + #[lints(min_ident_chars)] + min_ident_chars_threshold: u64 = 1, + /// Whether to **only** check for missing documentation in items visible within the current + /// crate. For example, `pub(crate)` items. + #[lints(missing_docs_in_private_items)] + missing_docs_in_crate_items: bool = false, + /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` + #[default_text = "current version"] + #[lints( + allow_attributes, + allow_attributes_without_reason, + almost_complete_range, + approx_constant, + assigning_clones, + borrow_as_ptr, + cast_abs_to_unsigned, + checked_conversions, + cloned_instead_of_copied, + collapsible_match, + collapsible_str_replace, + deprecated_cfg_attr, + derivable_impls, + err_expect, + filter_map_next, + from_over_into, + if_then_some_else_none, + index_refutable_slice, + iter_kv_map, + legacy_numeric_constants, + manual_bits, + manual_c_str_literals, + manual_clamp, + manual_hash_one, + manual_is_ascii_check, + manual_let_else, + manual_non_exhaustive, + manual_pattern_char_comparison, + manual_range_contains, + manual_rem_euclid, + manual_retain, + manual_split_once, + manual_str_repeat, + manual_strip, + manual_try_fold, + map_clone, + map_unwrap_or, + match_like_matches_macro, + mem_replace_with_default, + missing_const_for_fn, + needless_borrow, + option_as_ref_deref, + option_map_unwrap_or, + ptr_as_ptr, + redundant_field_names, + redundant_static_lifetimes, + seek_from_current, + seek_rewind, + transmute_ptr_to_ref, + tuple_array_conversions, + type_repetition_in_bounds, + unchecked_duration_subtraction, + uninlined_format_args, + unnecessary_lazy_evaluations, + unnested_or_patterns, + use_self, + )] + msrv: Msrv = Msrv::empty(), + /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. + #[lints(large_types_passed_by_value)] + pass_by_value_size_limit: u64 = 256, /// Lint "public" fields in a struct that are prefixed with an underscore based on their /// exported visibility, or whether they are marked as "pub". - (pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported), - /// Lint: MODULO_ARITHMETIC. - /// - /// Don't lint when comparing the result of a modulo operation to zero. - (allow_comparison_to_zero: bool = true), - /// Lint: WILDCARD_IMPORTS. - /// - /// List of path segments allowed to have wildcard imports. - /// - /// #### Example - /// - /// ```toml - /// allowed-wildcard-imports = [ "utils", "common" ] - /// ``` - /// - /// #### Noteworthy - /// - /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`. - /// 2. Paths with any segment that containing the word 'prelude' - /// are already allowed by default. - (allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default()), - /// Lint: MODULE_NAME_REPETITIONS. - /// - /// List of prefixes to allow when determining whether an item's name ends with the module's name. - /// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`), - /// then don't emit a warning. - /// - /// #### Example - /// - /// ```toml - /// allowed-prefixes = [ "to", "from" ] - /// ``` - /// - /// #### Noteworthy - /// - /// - By default, the following prefixes are allowed: `to`, `as`, `into`, `from`, `try_into` and `try_from` - /// - PascalCase variant is included automatically for each snake_case variant (e.g. if `try_into` is included, - /// `TryInto` will also be included) - /// - Use `".."` as part of the list to indicate that the configured values should be appended to the - /// default configuration of Clippy. By default, any configuration will replace the default value - (allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()), - /// Lint: RENAMED_FUNCTION_PARAMS. - /// - /// List of trait paths to ignore when checking renamed function parameters. - /// - /// #### Example - /// - /// ```toml - /// allow-renamed-params-for = [ "std::convert::From" ] - /// ``` - /// - /// #### Noteworthy - /// - /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr` - /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the - /// default configuration of Clippy. By default, any configuration will replace the default value. - (allow_renamed_params_for: Vec<String> = - DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()), - /// Lint: MACRO_METAVARS_IN_UNSAFE. + #[lints(pub_underscore_fields)] + pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported, + /// Whether to lint only if it's multiline. + #[lints(semicolon_inside_block)] + semicolon_inside_block_ignore_singleline: bool = false, + /// Whether to lint only if it's singleline. + #[lints(semicolon_outside_block)] + semicolon_outside_block_ignore_multiline: bool = false, + /// The maximum number of single char bindings a scope may have + #[lints(many_single_char_names)] + single_char_binding_names_threshold: u64 = 4, + /// The maximum allowed stack size for functions in bytes + #[lints(large_stack_frames)] + stack_size_threshold: u64 = 512_000, + /// Enforce the named macros always use the braces specified. /// + /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro + /// could be used with a full path two `MacroMatcher`s have to be added one with the full path + /// `crate_name::macro_name` and one with just the macro name. + #[lints(nonstandard_macro_braces)] + standard_macro_braces: Vec<MacroMatcher> = Vec::new(), + /// The minimum number of struct fields for the lints about field names to trigger + #[lints(struct_field_names)] + struct_field_name_threshold: u64 = 3, + /// Whether to suppress a restriction lint in constant code. In same + /// cases the restructured operation might not be unavoidable, as the + /// suggested counterparts are unavailable in constant code. This + /// configuration will cause restriction lints to trigger even + /// if no suggestion can be made. + #[lints(indexing_slicing)] + suppress_restriction_lint_in_const: bool = false, + /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap + #[lints(boxed_local, useless_vec)] + too_large_for_stack: u64 = 200, + /// The maximum number of argument a function or method can have + #[lints(too_many_arguments)] + too_many_arguments_threshold: u64 = 7, + /// The maximum number of lines a function or method can have + #[lints(too_many_lines)] + too_many_lines_threshold: u64 = 100, + /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by + /// reference. By default there is no limit + #[default_text = "target_pointer_width * 2"] + #[lints(trivially_copy_pass_by_ref)] + trivial_copy_size_limit: Option<u64> = None, + /// The maximum complexity a type can have + #[lints(type_complexity)] + type_complexity_threshold: u64 = 250, + /// The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint + #[lints(unnecessary_box_returns)] + unnecessary_box_size: u64 = 128, + /// Should the fraction of a decimal be linted to include separators. + #[lints(unreadable_literal)] + unreadable_literal_lint_fractions: bool = true, + /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other + #[lints(upper_case_acronyms)] + upper_case_acronyms_aggressive: bool = false, + /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed + #[lints(vec_box)] + vec_box_size_threshold: u64 = 4096, + /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' + #[lints(verbose_bit_mask)] + verbose_bit_mask_threshold: u64 = 1, + /// Whether to allow certain wildcard imports (prelude, super in tests). + #[lints(wildcard_imports)] + warn_on_all_wildcard_imports: bool = false, /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. - (warn_unsafe_macro_metavars_in_private_macros: bool = false), + #[lints(macro_metavars_in_unsafe)] + warn_unsafe_macro_metavars_in_private_macros: bool = false, } /// Search for the configuration file. diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index ff7fa7241cb96..d2246f12029b3 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -1,5 +1,4 @@ -#![feature(rustc_private, let_chains)] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![feature(rustc_private, array_windows, let_chains)] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/clippy_config/src/metadata.rs b/clippy_config/src/metadata.rs index 400887185e8cf..7cbd92a9b7100 100644 --- a/clippy_config/src/metadata.rs +++ b/clippy_config/src/metadata.rs @@ -1,11 +1,12 @@ -use std::fmt::{self, Write}; +use itertools::Itertools; +use std::fmt; #[derive(Debug, Clone, Default)] pub struct ClippyConfiguration { pub name: String, pub default: String, - pub lints: Vec<String>, - pub doc: String, + pub lints: &'static [&'static str], + pub doc: &'static str, pub deprecation_reason: Option<&'static str>, } @@ -13,61 +14,23 @@ impl fmt::Display for ClippyConfiguration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "- `{}`: {}", self.name, self.doc)?; if !self.default.is_empty() { - write!(f, " (default: `{}`)", self.default)?; + write!(f, "\n\n (default: `{}`)", self.default)?; } Ok(()) } } impl ClippyConfiguration { - pub fn new( - name: &'static str, - default: String, - doc_comment: &'static str, - deprecation_reason: Option<&'static str>, - ) -> Self { - let (mut lints, doc) = parse_config_field_doc(doc_comment) - .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string())); - - lints.sort(); - - Self { - name: to_kebab(name), - lints, - doc, - default, - deprecation_reason, - } - } - pub fn to_markdown_paragraph(&self) -> String { - let mut out = format!( - "## `{}`\n{}\n\n", + format!( + "## `{}`\n{}\n\n**Default Value:** `{}`\n\n---\n**Affected lints:**\n{}\n\n", self.name, - self.doc - .lines() - .map(|line| line.strip_prefix(" ").unwrap_or(line)) - .collect::<Vec<_>>() - .join("\n"), - ); - - if !self.default.is_empty() { - write!(out, "**Default Value:** `{}`\n\n", self.default).unwrap(); - } - - write!( - out, - "---\n**Affected lints:**\n{}\n\n", - self.lints - .iter() - .map(|name| name.to_string().split_whitespace().next().unwrap().to_string()) - .map(|name| format!("* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})")) - .collect::<Vec<_>>() - .join("\n"), + self.doc.lines().map(|x| x.strip_prefix(' ').unwrap_or(x)).join("\n"), + self.default, + self.lints.iter().format_with("\n", |name, f| f(&format_args!( + "* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})" + ))), ) - .unwrap(); - - out } pub fn to_markdown_link(&self) -> String { @@ -75,47 +38,3 @@ impl ClippyConfiguration { format!("[`{}`]: {BOOK_CONFIGS_PATH}#{}", self.name, self.name) } } - -/// This parses the field documentation of the config struct. -/// -/// ```rust, ignore -/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin") -/// ``` -/// -/// Would yield: -/// ```rust, ignore -/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin") -/// ``` -fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> { - const DOC_START: &str = " Lint: "; - if doc_comment.starts_with(DOC_START) - && let Some(split_pos) = doc_comment.find('.') - { - let mut doc_comment = doc_comment.to_string(); - let mut documentation = doc_comment.split_off(split_pos); - - // Extract lints - doc_comment.make_ascii_lowercase(); - let lints: Vec<String> = doc_comment - .split_off(DOC_START.len()) - .lines() - .next() - .unwrap() - .split(", ") - .map(str::to_string) - .collect(); - - // Format documentation correctly - // split off leading `.` from lint name list and indent for correct formatting - documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n "); - - Some((lints, documentation)) - } else { - None - } -} - -/// Transforms a given `snake_case_string` to a tasty `kebab-case-string` -fn to_kebab(config_name: &str) -> String { - config_name.replace('_', "-") -} diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 4104e7d94f146..a5d72c3a559df 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -13,9 +13,6 @@ opener = "0.6" shell-escape = "0.1" walkdir = "2.3" -[features] -deny-warnings = [] - [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] rustc_private = true diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 2562314418172..5fc4365c6e785 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -1,30 +1,65 @@ use crate::clippy_project_root; use itertools::Itertools; +use rustc_lexer::{tokenize, TokenKind}; use shell_escape::escape; use std::ffi::{OsStr, OsString}; -use std::path::Path; +use std::ops::ControlFlow; +use std::path::{Path, PathBuf}; use std::process::{self, Command, Stdio}; use std::{fs, io}; use walkdir::WalkDir; -#[derive(Debug)] -pub enum CliError { +pub enum Error { CommandFailed(String, String), - IoError(io::Error), + Io(io::Error), RustfmtNotInstalled, - WalkDirError(walkdir::Error), + WalkDir(walkdir::Error), IntellijSetupActive, + Parse(PathBuf, usize, String), + CheckFailed, } -impl From<io::Error> for CliError { +impl From<io::Error> for Error { fn from(error: io::Error) -> Self { - Self::IoError(error) + Self::Io(error) } } -impl From<walkdir::Error> for CliError { +impl From<walkdir::Error> for Error { fn from(error: walkdir::Error) -> Self { - Self::WalkDirError(error) + Self::WalkDir(error) + } +} + +impl Error { + fn display(&self) { + match self { + Self::CheckFailed => { + eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update."); + }, + Self::CommandFailed(command, stderr) => { + eprintln!("error: command `{command}` failed!\nstderr: {stderr}"); + }, + Self::Io(err) => { + eprintln!("error: {err}"); + }, + Self::RustfmtNotInstalled => { + eprintln!("error: rustfmt nightly is not installed."); + }, + Self::WalkDir(err) => { + eprintln!("error: {err}"); + }, + Self::IntellijSetupActive => { + eprintln!( + "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\ + Not formatting because that would format the local repo as well!\n\ + Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`." + ); + }, + Self::Parse(path, line, msg) => { + eprintln!("error parsing `{}:{line}`: {msg}", path.display()); + }, + } } } @@ -34,75 +69,244 @@ struct FmtContext { rustfmt_path: String, } -// the "main" function of cargo dev fmt -pub fn run(check: bool, verbose: bool) { - fn try_run(context: &FmtContext) -> Result<bool, CliError> { - let mut success = true; - - let project_root = clippy_project_root(); - - // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to - // format because rustfmt would also format the entire rustc repo as it is a local - // dependency - if fs::read_to_string(project_root.join("Cargo.toml")) - .expect("Failed to read clippy Cargo.toml") - .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") - { - return Err(CliError::IntellijSetupActive); - } - - rustfmt_test(context)?; +struct ClippyConf<'a> { + name: &'a str, + attrs: &'a str, + lints: Vec<&'a str>, + field: &'a str, +} - success &= cargo_fmt(context, project_root.as_path())?; - success &= cargo_fmt(context, &project_root.join("clippy_dev"))?; - success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?; - success &= cargo_fmt(context, &project_root.join("lintcheck"))?; +fn offset_to_line(text: &str, offset: usize) -> usize { + match text.split('\n').try_fold((1usize, 0usize), |(line, pos), s| { + let pos = pos + s.len() + 1; + if pos > offset { + ControlFlow::Break(line) + } else { + ControlFlow::Continue((line + 1, pos)) + } + }) { + ControlFlow::Break(x) | ControlFlow::Continue((x, _)) => x, + } +} - let chunks = WalkDir::new(project_root.join("tests")) - .into_iter() - .filter_map(|entry| { - let entry = entry.expect("failed to find tests"); - let path = entry.path(); +/// Formats the configuration list in `clippy_config/src/conf.rs` +#[expect(clippy::too_many_lines)] +fn fmt_conf(check: bool) -> Result<(), Error> { + #[derive(Clone, Copy)] + enum State { + Start, + Docs, + Pound, + OpenBracket, + Attr(u32), + Lints, + EndLints, + Field, + } - if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" { - None - } else { - Some(entry.into_path().into_os_string()) - } - }) - .chunks(250); + let path: PathBuf = [ + clippy_project_root().as_path(), + "clippy_config".as_ref(), + "src".as_ref(), + "conf.rs".as_ref(), + ] + .into_iter() + .collect(); + let text = fs::read_to_string(&path)?; - for chunk in &chunks { - success &= rustfmt(context, chunk)?; - } + let (pre, conf) = text + .split_once("define_Conf! {\n") + .expect("can't find config definition"); + let (conf, post) = conf.split_once("\n}\n").expect("can't find config definition"); + let conf_offset = pre.len() + 15; - Ok(success) - } + let mut pos = 0u32; + let mut attrs_start = 0; + let mut attrs_end = 0; + let mut field_start = 0; + let mut lints = Vec::new(); + let mut name = ""; + let mut fields = Vec::new(); + let mut state = State::Start; - fn output_err(err: CliError) { - match err { - CliError::CommandFailed(command, stderr) => { - eprintln!("error: A command failed! `{command}`\nstderr: {stderr}"); + for (i, t) in tokenize(conf) + .map(|x| { + let start = pos; + pos += x.len; + (start as usize, x) + }) + .filter(|(_, t)| !matches!(t.kind, TokenKind::Whitespace)) + { + match (state, t.kind) { + (State::Start, TokenKind::LineComment { doc_style: Some(_) }) => { + attrs_start = i; + attrs_end = i + t.len as usize; + state = State::Docs; }, - CliError::IoError(err) => { - eprintln!("error: {err}"); + (State::Start, TokenKind::Pound) => { + attrs_start = i; + attrs_end = i; + state = State::Pound; }, - CliError::RustfmtNotInstalled => { - eprintln!("error: rustfmt nightly is not installed."); + (State::Docs, TokenKind::LineComment { doc_style: Some(_) }) => attrs_end = i + t.len as usize, + (State::Docs, TokenKind::Pound) => state = State::Pound, + (State::Pound, TokenKind::OpenBracket) => state = State::OpenBracket, + (State::OpenBracket, TokenKind::Ident) => { + state = if conf[i..i + t.len as usize] == *"lints" { + State::Lints + } else { + State::Attr(0) + }; }, - CliError::WalkDirError(err) => { - eprintln!("error: {err}"); + (State::Attr(0), TokenKind::CloseBracket) => { + attrs_end = i + 1; + state = State::Docs; }, - CliError::IntellijSetupActive => { - eprintln!( - "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`. -Not formatting because that would format the local repo as well! -Please revert the changes to Cargo.tomls with `cargo dev remove intellij`." - ); + (State::Attr(x), TokenKind::OpenParen | TokenKind::OpenBracket | TokenKind::OpenBrace) => { + state = State::Attr(x + 1); + }, + (State::Attr(x), TokenKind::CloseParen | TokenKind::CloseBracket | TokenKind::CloseBrace) => { + state = State::Attr(x - 1); + }, + (State::Lints, TokenKind::Ident) => lints.push(&conf[i..i + t.len as usize]), + (State::Lints, TokenKind::CloseBracket) => state = State::EndLints, + (State::EndLints | State::Docs, TokenKind::Ident) => { + field_start = i; + name = &conf[i..i + t.len as usize]; + state = State::Field; + }, + (State::Field, TokenKind::LineComment { doc_style: Some(_) }) => { + #[expect(clippy::drain_collect)] + fields.push(ClippyConf { + name, + lints: lints.drain(..).collect(), + attrs: &conf[attrs_start..attrs_end], + field: conf[field_start..i].trim_end(), + }); + attrs_start = i; + attrs_end = i + t.len as usize; + state = State::Docs; + }, + (State::Field, TokenKind::Pound) => { + #[expect(clippy::drain_collect)] + fields.push(ClippyConf { + name, + lints: lints.drain(..).collect(), + attrs: &conf[attrs_start..attrs_end], + field: conf[field_start..i].trim_end(), + }); + attrs_start = i; + attrs_end = i; + state = State::Pound; + }, + (State::Field | State::Attr(_), _) + | (State::Lints, TokenKind::Comma | TokenKind::OpenParen | TokenKind::CloseParen) => {}, + _ => { + return Err(Error::Parse( + path, + offset_to_line(&text, conf_offset + i), + format!("unexpected token `{}`", &conf[i..i + t.len as usize]), + )); }, } } + if !matches!(state, State::Field) { + return Err(Error::Parse( + path, + offset_to_line(&text, conf_offset + conf.len()), + "incomplete field".into(), + )); + } + fields.push(ClippyConf { + name, + lints, + attrs: &conf[attrs_start..attrs_end], + field: conf[field_start..].trim_end(), + }); + + for field in &mut fields { + field.lints.sort_unstable(); + } + fields.sort_by_key(|x| x.name); + + let new_text = format!( + "{pre}define_Conf! {{\n{}}}\n{post}", + fields.iter().format_with("", |field, f| { + if field.lints.is_empty() { + f(&format_args!(" {}\n {}\n", field.attrs, field.field)) + } else if field.lints.iter().map(|x| x.len() + 2).sum::<usize>() < 120 - 14 { + f(&format_args!( + " {}\n #[lints({})]\n {}\n", + field.attrs, + field.lints.iter().join(", "), + field.field, + )) + } else { + f(&format_args!( + " {}\n #[lints({}\n )]\n {}\n", + field.attrs, + field + .lints + .iter() + .format_with("", |x, f| f(&format_args!("\n {x},"))), + field.field, + )) + } + }) + ); + + if text != new_text { + if check { + return Err(Error::CheckFailed); + } + fs::write(&path, new_text.as_bytes())?; + } + Ok(()) +} + +fn run_rustfmt(context: &FmtContext) -> Result<(), Error> { + let project_root = clippy_project_root(); + + // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to + // format because rustfmt would also format the entire rustc repo as it is a local + // dependency + if fs::read_to_string(project_root.join("Cargo.toml")) + .expect("Failed to read clippy Cargo.toml") + .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") + { + return Err(Error::IntellijSetupActive); + } + + check_for_rustfmt(context)?; + + cargo_fmt(context, project_root.as_path())?; + cargo_fmt(context, &project_root.join("clippy_dev"))?; + cargo_fmt(context, &project_root.join("rustc_tools_util"))?; + cargo_fmt(context, &project_root.join("lintcheck"))?; + + let chunks = WalkDir::new(project_root.join("tests")) + .into_iter() + .filter_map(|entry| { + let entry = entry.expect("failed to find tests"); + let path = entry.path(); + + if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" { + None + } else { + Some(entry.into_path().into_os_string()) + } + }) + .chunks(250); + + for chunk in &chunks { + rustfmt(context, chunk)?; + } + Ok(()) +} + +// the "main" function of cargo dev fmt +pub fn run(check: bool, verbose: bool) { let output = Command::new("rustup") .args(["which", "rustfmt"]) .stderr(Stdio::inherit()) @@ -120,21 +324,10 @@ Please revert the changes to Cargo.tomls with `cargo dev remove intellij`." verbose, rustfmt_path, }; - let result = try_run(&context); - let code = match result { - Ok(true) => 0, - Ok(false) => { - eprintln!(); - eprintln!("Formatting check failed."); - eprintln!("Run `cargo dev fmt` to update formatting."); - 1 - }, - Err(err) => { - output_err(err); - 1 - }, - }; - process::exit(code); + if let Err(e) = run_rustfmt(&context).and_then(|()| fmt_conf(check)) { + e.display(); + process::exit(1); + } } fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String { @@ -148,12 +341,12 @@ fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[imp ) } -fn exec( +fn exec_fmt_command( context: &FmtContext, program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>], -) -> Result<bool, CliError> { +) -> Result<(), Error> { if context.verbose { println!("{}", format_command(&program, &dir, args)); } @@ -166,28 +359,28 @@ fn exec( .unwrap(); let success = output.status.success(); - if !context.check && !success { - let stderr = std::str::from_utf8(&output.stderr).unwrap_or(""); - return Err(CliError::CommandFailed( - format_command(&program, &dir, args), - String::from(stderr), - )); + match (context.check, success) { + (_, true) => Ok(()), + (true, false) => Err(Error::CheckFailed), + (false, false) => { + let stderr = std::str::from_utf8(&output.stderr).unwrap_or(""); + Err(Error::CommandFailed( + format_command(&program, &dir, args), + String::from(stderr), + )) + }, } - - Ok(success) } -fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> { +fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<(), Error> { let mut args = vec!["fmt", "--all"]; if context.check { args.push("--check"); } - let success = exec(context, "cargo", path, &args)?; - - Ok(success) + exec_fmt_command(context, "cargo", path, &args) } -fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> { +fn check_for_rustfmt(context: &FmtContext) -> Result<(), Error> { let program = "rustfmt"; let dir = std::env::current_dir()?; let args = &["--version"]; @@ -204,23 +397,20 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> { .unwrap_or("") .starts_with("error: 'rustfmt' is not installed") { - Err(CliError::RustfmtNotInstalled) + Err(Error::RustfmtNotInstalled) } else { - Err(CliError::CommandFailed( + Err(Error::CommandFailed( format_command(program, &dir, args), std::str::from_utf8(&output.stderr).unwrap_or("").to_string(), )) } } -fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<bool, CliError> { +fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<(), Error> { let mut args = Vec::new(); if context.check { args.push(OsString::from("--check")); } args.extend(paths); - - let success = exec(context, &context.rustfmt_path, std::env::current_dir()?, &args)?; - - Ok(success) + exec_fmt_command(context, &context.rustfmt_path, std::env::current_dir()?, &args) } diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 3aa43dbe23ed6..ad385d5fbd29f 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,6 +1,5 @@ #![feature(let_chains)] #![feature(rustc_private)] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 366b52b25dfcf..755b04b0b232f 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -1,4 +1,3 @@ -#![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)] @@ -74,7 +73,7 @@ fn main() { new_name, uplift, } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift), - DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, reason.as_deref()), + DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason), } } @@ -223,7 +222,7 @@ enum DevCommand { name: String, #[arg(long, short)] /// The reason for deprecation - reason: Option<String>, + reason: String, }, } diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index 4a4261d1a1e63..19560b31fd3e2 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -1,10 +1,14 @@ -use std::ffi::OsStr; -use std::num::ParseIntError; use std::path::Path; use std::process::Command; use std::time::{Duration, SystemTime}; use std::{env, thread}; +#[cfg(windows)] +const PYTHON: &str = "python"; + +#[cfg(not(windows))] +const PYTHON: &str = "python3"; + /// # Panics /// /// Panics if the python commands could not be spawned @@ -25,7 +29,7 @@ pub fn run(port: u16, lint: Option<String>) -> ! { } if let Some(url) = url.take() { thread::spawn(move || { - Command::new("python3") + Command::new(PYTHON) .arg("-m") .arg("http.server") .arg(port.to_string()) @@ -58,8 +62,3 @@ fn mtime(path: impl AsRef<Path>) -> SystemTime { .unwrap_or(SystemTime::UNIX_EPOCH) } } - -#[allow(clippy::missing_errors_doc)] -pub fn validate_port(arg: &OsStr) -> Result<(), ParseIntError> { - arg.to_string_lossy().parse::<u16>().map(|_| ()) -} diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 45353901c98fd..15578d69c3a9d 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,13 +1,12 @@ use crate::clippy_project_root; use aho_corasick::AhoCorasickBuilder; -use indoc::writedoc; use itertools::Itertools; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fmt::{self, Write}; use std::fs::{self, OpenOptions}; -use std::io::{self, Read, Seek, SeekFrom, Write as _}; +use std::io::{self, Read, Seek, Write as _}; use std::ops::Range; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; @@ -77,12 +76,8 @@ fn generate_lint_files( for lint in usable_lints .iter() .map(|l| &*l.name) - .chain(deprecated_lints.iter().map(|l| &*l.name)) - .chain( - renamed_lints - .iter() - .map(|l| l.old_name.strip_prefix("clippy::").unwrap_or(&l.old_name)), - ) + .chain(deprecated_lints.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) + .chain(renamed_lints.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) .sorted() { writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); @@ -108,11 +103,6 @@ fn generate_lint_files( update_mode, &gen_declared_lints(internal_lints.iter(), usable_lints.iter()), ); - process_file( - "clippy_lints/src/lib.deprecated.rs", - update_mode, - &gen_deprecated(deprecated_lints), - ); let content = gen_deprecated_lints_test(deprecated_lints); process_file("tests/ui/deprecated.rs", update_mode, &content); @@ -205,7 +195,7 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { let ext = f.path().extension(); (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) && name != Some(OsStr::new("rename.rs")) - && name != Some(OsStr::new("renamed_lints.rs")) + && name != Some(OsStr::new("deprecated_lints.rs")) }) { rewrite_file(file.path(), |s| { @@ -213,6 +203,19 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { }); } + let version = crate::new_lint::get_stabilization_version(); + rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| { + insert_at_marker( + s, + "// end renamed lints. used by `cargo dev rename_lint`", + &format!( + "#[clippy::version = \"{version}\"]\n \ + (\"{}\", \"{}\"),\n ", + lint.old_name, lint.new_name, + ), + ) + }); + renamed_lints.push(lint); renamed_lints.sort_by(|lhs, rhs| { lhs.new_name @@ -222,11 +225,6 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { .then_with(|| lhs.old_name.cmp(&rhs.old_name)) }); - write_file( - Path::new("clippy_lints/src/renamed_lints.rs"), - &gen_renamed_lints_list(&renamed_lints), - ); - if uplift { write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); println!( @@ -293,7 +291,8 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being // renamed. - for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) { + for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("deprecated_lints.rs")) + { rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); } @@ -304,7 +303,6 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { println!("note: `cargo uitest` still needs to be run to update the test results"); } -const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note"; /// Runs the `deprecate` command /// /// This does the following: @@ -314,33 +312,16 @@ const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note"; /// # Panics /// /// If a file path could not read from or written to -pub fn deprecate(name: &str, reason: Option<&str>) { - fn finish( - (lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>), - name: &str, - reason: &str, - ) { - deprecated_lints.push(DeprecatedLint { - name: name.to_string(), - reason: reason.to_string(), - declaration_range: Range::default(), - }); - - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("info: `{name}` has successfully been deprecated"); - - if reason == DEFAULT_DEPRECATION_REASON { - println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`"); - } - println!("note: you must run `cargo uitest` to update the test results"); - } - - let reason = reason.unwrap_or(DEFAULT_DEPRECATION_REASON); - let name_lower = name.to_lowercase(); - let name_upper = name.to_uppercase(); +pub fn deprecate(name: &str, reason: &str) { + let prefixed_name = if name.starts_with("clippy::") { + name.to_owned() + } else { + format!("clippy::{name}") + }; + let stripped_name = &prefixed_name[8..]; - let (mut lints, deprecated_lints, renamed_lints) = gather_all(); - let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { + let (mut lints, mut deprecated_lints, renamed_lints) = gather_all(); + let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { eprintln!("error: failed to find lint `{name}`"); return; }; @@ -357,13 +338,27 @@ pub fn deprecate(name: &str, reason: Option<&str>) { let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs"); - if remove_lint_declaration(&name_lower, &mod_path, &mut lints).unwrap_or(false) { - declare_deprecated(&name_upper, deprecated_lints_path, reason).unwrap(); - finish((lints, deprecated_lints, renamed_lints), name, reason); - return; - } + if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { + let version = crate::new_lint::get_stabilization_version(); + rewrite_file(deprecated_lints_path, |s| { + insert_at_marker( + s, + "// end deprecated lints. used by `cargo dev deprecate_lint`", + &format!("#[clippy::version = \"{version}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ",), + ) + }); + + deprecated_lints.push(DeprecatedLint { + name: prefixed_name, + reason: reason.into(), + }); - eprintln!("error: lint not found"); + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("info: `{name}` has successfully been deprecated"); + println!("note: you must run `cargo uitest` to update the test results"); + } else { + eprintln!("error: lint not found"); + } } fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> { @@ -377,14 +372,14 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io // Some lints have their own directories, delete them if path.is_dir() { - fs::remove_dir_all(path).ok(); + let _ = fs::remove_dir_all(path); return; } // Remove all related test files - fs::remove_file(path.with_extension("rs")).ok(); - fs::remove_file(path.with_extension("stderr")).ok(); - fs::remove_file(path.with_extension("fixed")).ok(); + let _ = fs::remove_file(path.with_extension("rs")); + let _ = fs::remove_file(path.with_extension("stderr")); + let _ = fs::remove_file(path.with_extension("fixed")); } fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { @@ -427,7 +422,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io lint_mod_path.set_file_name(name); lint_mod_path.set_extension("rs"); - fs::remove_file(lint_mod_path).ok(); + let _ = fs::remove_file(lint_mod_path); } let mut content = @@ -465,37 +460,6 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io Ok(false) } -fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> { - let mut file = OpenOptions::new().write(true).open(path)?; - - file.seek(SeekFrom::End(0))?; - - let version = crate::new_lint::get_stabilization_version(); - let deprecation_reason = if reason == DEFAULT_DEPRECATION_REASON { - "TODO" - } else { - reason - }; - - writedoc!( - file, - " - - declare_deprecated_lint! {{ - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// {deprecation_reason} - #[clippy::version = \"{version}\"] - pub {name}, - \"{reason}\" - }} - - " - ) -} - /// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there /// were no replacements. fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> { @@ -604,14 +568,12 @@ impl Lint { struct DeprecatedLint { name: String, reason: String, - declaration_range: Range<usize>, } impl DeprecatedLint { - fn new(name: &str, reason: &str, declaration_range: Range<usize>) -> Self { + fn new(name: &str, reason: &str) -> Self { Self { - name: name.to_lowercase(), + name: remove_line_splices(name), reason: remove_line_splices(reason), - declaration_range, } } } @@ -629,28 +591,6 @@ impl RenamedLint { } } -/// Generates the `register_removed` code -#[must_use] -fn gen_deprecated(lints: &[DeprecatedLint]) -> String { - let mut output = GENERATED_FILE_COMMENT.to_string(); - output.push_str("{\n"); - for lint in lints { - let _: fmt::Result = write!( - output, - concat!( - " store.register_removed(\n", - " \"clippy::{}\",\n", - " \"{}\",\n", - " );\n" - ), - lint.name, lint.reason, - ); - } - output.push_str("}\n"); - - output -} - /// Generates the code for registering lints #[must_use] fn gen_declared_lints<'a>( @@ -680,7 +620,7 @@ fn gen_declared_lints<'a>( fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { let mut res: String = GENERATED_FILE_COMMENT.into(); for lint in lints { - writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap(); + writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); } res.push_str("\nfn main() {}\n"); res @@ -699,27 +639,13 @@ fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { seen_lints.clear(); for lint in lints { if seen_lints.insert(&lint.old_name) { - writeln!(res, "#![warn({})]", lint.old_name).unwrap(); + writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); } } res.push_str("\nfn main() {}\n"); res } -fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String { - const HEADER: &str = "\ - // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\ - #[rustfmt::skip]\n\ - pub static RENAMED_LINTS: &[(&str, &str)] = &[\n"; - - let mut res = String::from(HEADER); - for lint in lints { - writeln!(res, " (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap(); - } - res.push_str("];\n"); - res -} - /// Gathers all lints defined in `clippy_lints/src` fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) { let mut lints = Vec::with_capacity(1000); @@ -744,10 +670,10 @@ fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) { module.strip_suffix(".rs").unwrap_or(&module) }; - match module { - "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints), - "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints), - _ => parse_contents(&contents, module, &mut lints), + if module == "deprecated_lints" { + parse_deprecated_contents(&contents, &mut deprecated_lints, &mut renamed_lints); + } else { + parse_contents(&contents, module, &mut lints); } } (lints, deprecated_lints, renamed_lints) @@ -848,54 +774,37 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) { } /// Parse a source file looking for `declare_deprecated_lint` macro invocations. -fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) { - let mut offset = 0usize; - let mut iter = tokenize(contents).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; - - LintDeclSearchResult { - token_kind: t.kind, - content: &contents[range.clone()], - range, - } - }); +fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint>, renamed: &mut Vec<RenamedLint>) { + let Some((_, contents)) = contents.split_once("\ndeclare_with_version! { DEPRECATED") else { + return; + }; + let Some((deprecated_src, renamed_src)) = contents.split_once("\ndeclare_with_version! { RENAMED") else { + return; + }; - while let Some(LintDeclSearchResult { range, .. }) = iter.find( - |LintDeclSearchResult { - token_kind, content, .. - }| token_kind == &TokenKind::Ident && *content == "declare_deprecated_lint", - ) { - let start = range.start; + for line in deprecated_src.lines() { + let mut offset = 0usize; + let mut iter = tokenize(line).map(|t| { + let range = offset..offset + t.len as usize; + offset = range.end; - let mut iter = iter.by_ref().filter(|LintDeclSearchResult { ref token_kind, .. }| { - !matches!(token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }) + LintDeclSearchResult { + token_kind: t.kind, + content: &line[range.clone()], + range, + } }); + let (name, reason) = match_tokens!( iter, - // !{ - Bang OpenBrace - // #[clippy::version = "version"] - Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket - // pub LINT_NAME, - Ident Ident(name) Comma - // "description" - Literal{kind: LiteralKind::Str{..},..}(reason) + // ("old_name", + Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(name) Comma + // "new_name"), + Whitespace Literal{kind: LiteralKind::Str{..},..}(reason) CloseParen Comma ); - - if let Some(LintDeclSearchResult { - token_kind: TokenKind::CloseBrace, - range, - .. - }) = iter.next() - { - lints.push(DeprecatedLint::new(name, reason, start..range.end)); - } + deprecated.push(DeprecatedLint::new(name, reason)); } -} - -fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) { - for line in contents.lines() { + for line in renamed_src.lines() { let mut offset = 0usize; let mut iter = tokenize(line).map(|t| { let range = offset..offset + t.len as usize; @@ -915,7 +824,7 @@ fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) { // "new_name"), Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma ); - lints.push(RenamedLint::new(old_name, new_name)); + renamed.push(RenamedLint::new(old_name, new_name)); } } @@ -1015,6 +924,12 @@ fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { panic!("failed to {action} file `{}`: {error}", name.display()) } +fn insert_at_marker(text: &str, marker: &str, new_text: &str) -> Option<String> { + let i = text.find(marker)?; + let (pre, post) = text.split_at(i); + Some([pre, new_text, post].into_iter().collect()) +} + fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) { let mut file = OpenOptions::new() .write(true) @@ -1084,31 +999,6 @@ mod tests { assert_eq!(expected, result); } - #[test] - fn test_parse_deprecated_contents() { - static DEPRECATED_CONTENTS: &str = r#" - /// some doc comment - declare_deprecated_lint! { - #[clippy::version = "I'm a version"] - pub SHOULD_ASSERT_EQ, - "`assert!()` will be more flexible with RFC 2011" - } - "#; - - let mut result = Vec::new(); - parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result); - for r in &mut result { - r.declaration_range = Range::default(); - } - - let expected = vec![DeprecatedLint::new( - "should_assert_eq", - "\"`assert!()` will be more flexible with RFC 2011\"", - Range::default(), - )]; - assert_eq!(expected, result); - } - #[test] fn test_usable_lints() { let lints = vec![ @@ -1177,34 +1067,4 @@ mod tests { ); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); } - - #[test] - fn test_gen_deprecated() { - let lints = vec![ - DeprecatedLint::new( - "should_assert_eq", - "\"has been superseded by should_assert_eq2\"", - Range::default(), - ), - DeprecatedLint::new("another_deprecated", "\"will be removed\"", Range::default()), - ]; - - let expected = GENERATED_FILE_COMMENT.to_string() - + &[ - "{", - " store.register_removed(", - " \"clippy::should_assert_eq\",", - " \"has been superseded by should_assert_eq2\",", - " );", - " store.register_removed(", - " \"clippy::another_deprecated\",", - " \"will be removed\",", - " );", - "}", - ] - .join("\n") - + "\n"; - - assert_eq!(expected, gen_deprecated(&lints)); - } } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index eb04c006f89fd..99ed93468a01a 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -32,7 +32,6 @@ url = "2.2" walkdir = "2.3" [features] -deny-warnings = ["clippy_config/deny-warnings", "clippy_utils/deny-warnings"] # build clippy with internal lints enabled, off by default internal = ["serde_json", "tempfile", "regex"] diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index e6d52bcef717c..3b4cc1134802d 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -97,7 +97,7 @@ impl ApproxConstant { cx, APPROX_CONSTANT, e.span, - format!("approximate value of `{module}::consts::{}` found", &name), + format!("approximate value of `{module}::consts::{name}` found"), None, "consider using the constant directly", ); diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs index cfa25005a05ed..fefd8195f8e7e 100644 --- a/clippy_lints/src/as_conversions.rs +++ b/clippy_lints/src/as_conversions.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -52,13 +52,15 @@ impl<'tcx> LateLintPass<'tcx> for AsConversions { && !in_external_macro(cx.sess(), expr.span) && !is_from_proc_macro(cx, expr) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, AS_CONVERSIONS, expr.span, "using a potentially dangerous silent `as` conversion", - None, - "consider using a safe wrapper for this conversion", + |diag| { + diag.help("consider using a safe wrapper for this conversion"); + }, ); } } diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs index 0db1456d40bfc..69a8eb7d94e74 100644 --- a/clippy_lints/src/asm_syntax.rs +++ b/clippy_lints/src/asm_syntax.rs @@ -1,6 +1,6 @@ use std::fmt; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions}; use rustc_ast::{InlineAsm, Item, ItemKind}; use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; @@ -49,14 +49,10 @@ fn check_asm_syntax( }; if style == check_for { - span_lint_and_help( - cx, - lint, - span, - format!("{style} x86 assembly syntax used"), - None, - format!("use {} x86 assembly syntax", !style), - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, lint, span, format!("{style} x86 assembly syntax used"), |diag| { + diag.help(format!("use {} x86 assembly syntax", !style)); + }); } } } @@ -98,13 +94,13 @@ declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]); impl EarlyLintPass for InlineAsmX86IntelSyntax { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::InlineAsm(inline_asm) = &expr.kind { - check_asm_syntax(Self::get_lints()[0], cx, inline_asm, expr.span, AsmStyle::Intel); + check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel); } } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if let ItemKind::GlobalAsm(inline_asm) = &item.kind { - check_asm_syntax(Self::get_lints()[0], cx, inline_asm, item.span, AsmStyle::Intel); + check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel); } } } @@ -146,13 +142,13 @@ declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]); impl EarlyLintPass for InlineAsmX86AttSyntax { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::InlineAsm(inline_asm) = &expr.kind { - check_asm_syntax(Self::get_lints()[0], cx, inline_asm, expr.span, AsmStyle::Att); + check_asm_syntax(INLINE_ASM_X86_ATT_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Att); } } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if let ItemKind::GlobalAsm(inline_asm) = &item.kind { - check_asm_syntax(Self::get_lints()[0], cx, inline_asm, item.span, AsmStyle::Att); + check_asm_syntax(INLINE_ASM_X86_ATT_SYNTAX, cx, inline_asm, item.span, AsmStyle::Att); } } } diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index ed4cdce8cb886..7eaac80f969f7 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_inside_always_const_context; use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return; }; - let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else { + let Some(Constant::Bool(val)) = ConstEvalCtxt::new(cx).eval(condition) else { return; }; diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index 7217686dcca5b..f1cb4a05af86c 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; @@ -68,39 +68,28 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { return; } } - let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" }; - let mut app = Applicability::MachineApplicable; - match method_segment.ident.as_str() { + let (message, replacement) = match method_segment.ident.as_str() { "is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => { - span_lint_and_sugg( - cx, - ASSERTIONS_ON_RESULT_STATES, - macro_call.span, - "called `assert!` with `Result::is_ok`", - "replace with", - format!( - "{}.unwrap(){semicolon}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 - ), - app, - ); + ("called `assert!` with `Result::is_ok`", "unwrap") }, "is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => { - span_lint_and_sugg( - cx, - ASSERTIONS_ON_RESULT_STATES, - macro_call.span, - "called `assert!` with `Result::is_err`", - "replace with", - format!( - "{}.unwrap_err(){semicolon}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 - ), - app, - ); + ("called `assert!` with `Result::is_err`", "unwrap_err") }, - _ => (), + _ => return, }; + span_lint_and_then(cx, ASSERTIONS_ON_RESULT_STATES, macro_call.span, message, |diag| { + let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" }; + let mut app = Applicability::MachineApplicable; + diag.span_suggestion( + macro_call.span, + "replace with", + format!( + "{}.{replacement}(){semicolon}", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 + ), + app, + ); + }); } } } diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 03f777600f084..6e336efbb90a2 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -227,9 +227,22 @@ fn build_sugg<'tcx>( match call_kind { CallKind::Method => { let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { - // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` - Sugg::hir_with_applicability(cx, ref_expr, "_", app) + // If `ref_expr` is a reference, we can remove the dereference operator (`*`) to make + // the generated code a bit simpler. In other cases, we don't do this special case, to avoid + // having to deal with Deref (https://github.com/rust-lang/rust-clippy/issues/12437). + + let ty = cx.typeck_results().expr_ty(ref_expr); + if ty.is_ref() { + // Apply special case, remove `*` + // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` + Sugg::hir_with_applicability(cx, ref_expr, "_", app) + } else { + // Keep the original lhs + // `*lhs = self_expr.clone();` -> `(*lhs).clone_from(self_expr)` + Sugg::hir_with_applicability(cx, lhs, "_", app) + } } else { + // Keep the original lhs // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` Sugg::hir_with_applicability(cx, lhs, "_", app) } @@ -249,8 +262,16 @@ fn build_sugg<'tcx>( }, CallKind::Ufcs => { let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { - // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)` - Sugg::hir_with_applicability(cx, ref_expr, "_", app) + // See special case of removing `*` in method handling above + let ty = cx.typeck_results().expr_ty(ref_expr); + if ty.is_ref() { + // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)` + Sugg::hir_with_applicability(cx, ref_expr, "_", app) + } else { + // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut *lhs, self_expr)` + // mut_addr_deref is used to avoid unnecessary parentheses around `*lhs` + Sugg::hir_with_applicability(cx, ref_expr, "_", app).mut_addr_deref() + } } else { // `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)` Sugg::hir_with_applicability(cx, lhs, "_", app).mut_addr() diff --git a/clippy_lints/src/attrs/allow_attributes.rs b/clippy_lints/src/attrs/allow_attributes.rs index df9994086cd48..a5a7b9f74a693 100644 --- a/clippy_lints/src/attrs/allow_attributes.rs +++ b/clippy_lints/src/attrs/allow_attributes.rs @@ -1,5 +1,5 @@ use super::ALLOW_ATTRIBUTES; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_ast::{AttrStyle, Attribute}; use rustc_errors::Applicability; @@ -13,14 +13,14 @@ pub fn check<'cx>(cx: &LateContext<'cx>, attr: &'cx Attribute) { && let Some(ident) = attr.ident() && !is_from_proc_macro(cx, attr) { - span_lint_and_sugg( - cx, - ALLOW_ATTRIBUTES, - ident.span, - "#[allow] attribute found", - "replace it with", - "expect".into(), - Applicability::MachineApplicable, - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, ALLOW_ATTRIBUTES, ident.span, "#[allow] attribute found", |diag| { + diag.span_suggestion( + ident.span, + "replace it with", + "expect", + Applicability::MachineApplicable, + ); + }); } } diff --git a/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/clippy_lints/src/attrs/allow_attributes_without_reason.rs index 4b42616a636bc..4ab97118df1d4 100644 --- a/clippy_lints/src/attrs/allow_attributes_without_reason.rs +++ b/clippy_lints/src/attrs/allow_attributes_without_reason.rs @@ -1,5 +1,5 @@ use super::{Attribute, ALLOW_ATTRIBUTES_WITHOUT_REASON}; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_lint::{LateContext, LintContext}; @@ -21,12 +21,14 @@ pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMet return; } - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, attr.span, format!("`{}` attribute without specifying a reason", name.as_str()), - None, - "try adding a reason at the end with `, reason = \"..\"`", + |diag| { + diag.help("try adding a reason at the end with `, reason = \"..\"`"); + }, ); } diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index 561ca9bd9866d..612712d16843c 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::Sugg; -use clippy_utils::{in_constant, is_else_clause}; +use clippy_utils::{is_else_clause, is_in_const_context}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { && let Some(else_lit) = as_int_bool_lit(else_) && then_lit != else_lit && !expr.span.from_expansion() - && !in_constant(cx, expr.hir_id) + && !is_in_const_context(cx) { let ty = cx.typeck_results().expr_ty(then); let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index a1c6c0b608f72..a2f48c18170a6 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -477,14 +477,12 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { cx: self.cx, }; if let Ok(expr) = h2q.run(e) { - if h2q.terminals.len() > 8 { - // QMC has exponentially slow behavior as the number of terminals increases - // 8 is reasonable, it takes approximately 0.2 seconds. - // See #825 + let stats = terminal_stats(&expr); + if stats.ops > 7 { + // QMC has exponentially slow behavior as the number of ops increases. + // See #825, #13206 return; } - - let stats = terminal_stats(&expr); let mut simplified = expr.simplify(); for simple in Bool::Not(Box::new(expr)).simplify() { match simple { diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index ff460a3fd8e39..bd3acc06f4b2b 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::in_constant; +use clippy_utils::is_in_const_context; use clippy_utils::source::snippet_opt; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_isize_or_usize; @@ -21,7 +21,7 @@ pub(super) fn check( cast_to_hir: &rustc_hir::Ty<'_>, msrv: &Msrv, ) { - if !should_lint(cx, expr, cast_from, cast_to, msrv) { + if !should_lint(cx, cast_from, cast_to, msrv) { return; } @@ -70,9 +70,9 @@ pub(super) fn check( ); } -fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { +fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). - if in_constant(cx, expr.hir_id) { + if is_in_const_context(cx) { return false; } diff --git a/clippy_lints/src/casts/cast_nan_to_int.rs b/clippy_lints/src/casts/cast_nan_to_int.rs index 5bc8692c289f7..464eabe5d9ab7 100644 --- a/clippy_lints/src/casts/cast_nan_to_int.rs +++ b/clippy_lints/src/casts/cast_nan_to_int.rs @@ -1,6 +1,6 @@ use super::CAST_NAN_TO_INT; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_note; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, } fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - match constant(cx, cx.typeck_results(), e) { + match ConstEvalCtxt::new(cx).eval(e) { // FIXME(f16_f128): add these types when nan checks are available on all platforms Some(Constant::F64(n)) => n.is_nan(), Some(Constant::F32(n)) => n.is_nan(), diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 7c5acd1a678d7..102fe25fc67b0 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::expr_or_init; use clippy_utils::source::snippet; @@ -15,7 +15,7 @@ use rustc_target::abi::IntegerType; use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> { - if let Some(Constant::Int(c)) = constant(cx, cx.typeck_results(), expr) { + if let Some(Constant::Int(c)) = ConstEvalCtxt::new(cx).eval(expr) { Some(c) } else { None diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 8bbd41b0db1ee..9daf237344a4b 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -1,7 +1,7 @@ use std::convert::Infallible; use std::ops::ControlFlow; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::visitors::{for_each_expr_without_closures, Descend}; use clippy_utils::{method_chain_args, sext}; @@ -88,7 +88,7 @@ fn get_const_signed_int_eval<'cx>( ) -> Option<i128> { let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr)); - if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)? + if let Constant::Int(n) = ConstEvalCtxt::new(cx).eval(expr)? && let ty::Int(ity) = *ty.kind() { return Some(sext(cx.tcx, n, ity)); @@ -103,7 +103,7 @@ fn get_const_unsigned_int_eval<'cx>( ) -> Option<u128> { let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr)); - if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)? + if let Constant::Int(n) = ConstEvalCtxt::new(cx).eval(expr)? && let ty::Uint(_ity) = *ty.kind() { return Some(n); diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs index 826589bf303b5..75de53f73ee7a 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs @@ -1,6 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -14,21 +14,24 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, _ => { /* continue to checks */ }, } - match cast_from.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { - let mut applicability = Applicability::MaybeIncorrect; - let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); + if let ty::FnDef(..) | ty::FnPtr(_) = cast_from.kind() { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); - span_lint_and_sugg( - cx, - FN_TO_NUMERIC_CAST_ANY, - expr.span, - format!("casting function pointer `{from_snippet}` to `{cast_to}`"), - "did you mean to invoke the function?", - format!("{from_snippet}() as {cast_to}"), - applicability, - ); - }, - _ => {}, + span_lint_and_then( + cx, + FN_TO_NUMERIC_CAST_ANY, + expr.span, + format!("casting function pointer `{from_snippet}` to `{cast_to}`"), + |diag| { + diag.span_suggestion_with_style( + expr.span, + "did you mean to invoke the function?", + format!("{from_snippet}() as {cast_to}"), + applicability, + SuggestionStyle::ShowAlways, + ); + }, + ); } } diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs index 5071af5ecb986..3c1c7d2dc3a54 100644 --- a/clippy_lints/src/casts/zero_ptr.rs +++ b/clippy_lints/src/casts/zero_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use clippy_utils::{in_constant, is_integer_literal, std_or_core}; +use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::ZERO_PTR; pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { if let TyKind::Ptr(ref mut_ty) = to.kind && is_integer_literal(from, 0) - && !in_constant(cx, from.hir_id) + && !is_in_const_context(cx) && let Some(std_or_core) = std_or_core(cx) { let (msg, sugg_fn) = match mut_ty.mutbl { diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 0b1ab5411bf18..1711565fca891 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -4,7 +4,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, is_integer_literal, SpanlessEq}; +use clippy_utils::{is_in_const_context, is_integer_literal, SpanlessEq}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { _ => return, } && !in_external_macro(cx.sess(), item.span) - && !in_constant(cx, item.hir_id) + && !is_in_const_context(cx) && self.msrv.meets(msrvs::TRY_FROM) && let Some(cv) = match op2 { // todo: check for case signed -> larger unsigned == only x >= 0 diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 2c23c0b4f154c..5d78744e9b5e2 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{if_sequence, in_constant, is_else_clause, SpanlessEq}; +use clippy_utils::{if_sequence, is_else_clause, is_in_const_context, SpanlessEq}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { return; } - if in_constant(cx, expr.hir_id) { + if is_in_const_context(cx) { return; } diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 27c00948a8f2b..b49a977dbeaf1 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,6 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use rustc_errors::Applicability; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -39,14 +39,24 @@ impl LateLintPass<'_> for CreateDir { && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id) { - span_lint_and_sugg( + span_lint_and_then( cx, CREATE_DIR, expr.span, "calling `std::fs::create_dir` where there may be a better way", - "consider calling `std::fs::create_dir_all` instead", - format!("create_dir_all({})", snippet(cx, arg.span, "..")), - Applicability::MaybeIncorrect, + |diag| { + let mut app = Applicability::MaybeIncorrect; + diag.span_suggestion_with_style( + expr.span, + "consider calling `std::fs::create_dir_all` instead", + format!( + "create_dir_all({})", + snippet_with_applicability(cx, arg.span, "..", &mut app) + ), + app, + SuggestionStyle::ShowAlways, + ); + }, ); } } diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index 788c6f3ada297..93c8fff05e9ed 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_test; use clippy_utils::macros::{macro_backtrace, MacroCall}; use clippy_utils::source::snippet_with_applicability; @@ -65,61 +65,67 @@ impl LateLintPass<'_> for DbgMacro { // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml !(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id)) { - let mut applicability = Applicability::MachineApplicable; - - let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { - // dbg!() - ExprKind::Block(..) => { - // If the `dbg!` macro is a "free" statement and not contained within other expressions, - // remove the whole statement. - if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) - && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) - { - (macro_call.span.to(semi_span), String::new()) - } else { - (macro_call.span, String::from("()")) - } - }, - // dbg!(1) - ExprKind::Match(val, ..) => ( - macro_call.span, - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string(), - ), - // dbg!(2, 3) - ExprKind::Tup( - [ - Expr { - kind: ExprKind::Match(first, ..), - .. - }, - .., - Expr { - kind: ExprKind::Match(last, ..), - .. - }, - ], - ) => { - let snippet = snippet_with_applicability( - cx, - first.span.source_callsite().to(last.span.source_callsite()), - "..", - &mut applicability, - ); - (macro_call.span, format!("({snippet})")) - }, - _ => return, - }; - self.prev_ctxt = cur_syntax_ctxt; - span_lint_and_sugg( + span_lint_and_then( cx, DBG_MACRO, - sugg_span, + macro_call.span, "the `dbg!` macro is intended as a debugging tool", - "remove the invocation before committing it to a version control system", - suggestion, - applicability, + |diag| { + let mut applicability = Applicability::MachineApplicable; + + let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { + // dbg!() + ExprKind::Block(..) => { + // If the `dbg!` macro is a "free" statement and not contained within other expressions, + // remove the whole statement. + if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) + && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) + { + (macro_call.span.to(semi_span), String::new()) + } else { + (macro_call.span, String::from("()")) + } + }, + // dbg!(1) + ExprKind::Match(val, ..) => ( + macro_call.span, + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) + .to_string(), + ), + // dbg!(2, 3) + ExprKind::Tup( + [ + Expr { + kind: ExprKind::Match(first, ..), + .. + }, + .., + Expr { + kind: ExprKind::Match(last, ..), + .. + }, + ], + ) => { + let snippet = snippet_with_applicability( + cx, + first.span.source_callsite().to(last.span.source_callsite()), + "..", + &mut applicability, + ); + (macro_call.span, format!("({snippet})")) + }, + _ => unreachable!(), + }; + + diag.span_suggestion( + sugg_span, + "remove the invocation before committing it to a version control system", + suggestion, + applicability, + ); + }, ); } } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 69f9eb6842bcd..3fb083dd83310 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -14,8 +14,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ #[cfg(feature = "internal")] crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO, #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON_INFO, - #[cfg(feature = "internal")] crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO, @@ -741,6 +739,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::unused_async::UNUSED_ASYNC_INFO, crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO, crate::unused_peekable::UNUSED_PEEKABLE_INFO, + crate::unused_result_ok::UNUSED_RESULT_OK_INFO, crate::unused_rounding::UNUSED_ROUNDING_INFO, crate::unused_self::UNUSED_SELF_INFO, crate::unused_unit::UNUSED_UNIT_INFO, diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 72fa05be3cc60..0b7279f2b360d 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -221,7 +221,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { .map(ToString::to_string) .collect::<Vec<_>>() .join(", "); - format!("{adt_def_ty_name}::<{}>", &tys_str) + format!("{adt_def_ty_name}::<{tys_str}>") } else { binding_type.to_string() }; diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 9af73db6849f4..a74b3a8c8362c 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -92,20 +92,8 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { let (suffix, is_float) = match lit_ty.kind() { ty::Int(IntTy::I32) => ("i32", false), ty::Float(FloatTy::F64) => ("f64", true), - // Default numeric fallback never results in other types. _ => return, }; - - let src = if let Some(src) = snippet_opt(self.cx, lit.span) { - src - } else { - match lit.node { - LitKind::Int(src, _) => format!("{src}"), - LitKind::Float(src, _) => format!("{src}"), - _ => return, - } - }; - let sugg = numeric_literal::format(&src, Some(suffix), is_float); span_lint_hir_and_then( self.cx, DEFAULT_NUMERIC_FALLBACK, @@ -113,6 +101,17 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { lit.span, "default numeric fallback might occur", |diag| { + let src = if let Some(src) = snippet_opt(self.cx, lit.span) { + src + } else { + match lit.node { + LitKind::Int(src, _) => format!("{src}"), + LitKind::Float(src, _) => format!("{src}"), + _ => unreachable!("Default numeric fallback never results in other types"), + } + }; + + let sugg = numeric_literal::format(&src, Some(suffix), is_float); diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect); }, ); diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index 3fa9bad0d03da..9f020d3081c40 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; @@ -56,16 +56,18 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { && is_union_with_two_non_zst_fields(cx, item) && !has_c_repr_attr(cx, item.hir_id()) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, DEFAULT_UNION_REPRESENTATION, item.span, "this union has the default representation", - None, - format!( - "consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout", - cx.tcx.def_path_str(item.owner_id) - ), + |diag| { + diag.help(format!( + "consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout", + cx.tcx.def_path_str(item.owner_id) + )); + }, ); } } diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index a0900f46f6aa6..0066ed643251a 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -1,243 +1,181 @@ -// NOTE: Entries should be created with `cargo dev deprecate` - -/// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This -/// enables the simple extraction of the metadata without changing the current deprecation -/// declaration. -pub struct ClippyDeprecatedLint { - #[allow(dead_code)] - pub desc: &'static str, -} - -#[macro_export] -macro_rules! declare_deprecated_lint { - { $(#[$attr:meta])* pub $name: ident, $reason: literal} => { - $(#[$attr])* - #[allow(dead_code)] - pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint { - desc: $reason - }; - } -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This used to check for `assert!(a == b)` and recommend - /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011. +// This file is managed by `cargo dev rename_lint` and `cargo dev deprecate_lint`. +// Prefer to use those when possible. + +macro_rules! declare_with_version { + ($name:ident($name_version:ident): &[$ty:ty] = &[$( + #[clippy::version = $version:literal] + $e:expr, + )*]) => { + pub static $name: &[$ty] = &[$($e),*]; + #[allow(unused)] + pub static $name_version: &[&str] = &[$($version),*]; + }; +} + +#[rustfmt::skip] +declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[ #[clippy::version = "pre 1.29.0"] - pub SHOULD_ASSERT_EQ, - "`assert!()` will be more flexible with RFC 2011" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This used to check for `Vec::extend`, which was slower than - /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true. + ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), #[clippy::version = "pre 1.29.0"] - pub EXTEND_FROM_SLICE, - "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// `Range::step_by(0)` used to be linted since it's - /// an infinite iterator, which is better expressed by `iter::repeat`, - /// but the method has been removed for `Iterator::step_by` which panics - /// if given a zero + ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), #[clippy::version = "pre 1.29.0"] - pub RANGE_STEP_BY_ZERO, - "`iterator.step_by(0)` panics nowadays" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This used to check for `Vec::as_slice`, which was unstable with good - /// stable alternatives. `Vec::as_slice` has now been stabilized. + ("clippy::range_step_by_zero", "`Iterator::step_by(0)` now panics and is no longer an infinite iterator"), #[clippy::version = "pre 1.29.0"] - pub UNSTABLE_AS_SLICE, - "`Vec::as_slice` has been stabilized in 1.7" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This used to check for `Vec::as_mut_slice`, which was unstable with good - /// stable alternatives. `Vec::as_mut_slice` has now been stabilized. + ("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"), #[clippy::version = "pre 1.29.0"] - pub UNSTABLE_AS_MUT_SLICE, - "`Vec::as_mut_slice` has been stabilized in 1.7" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint should never have applied to non-pointer types, as transmuting - /// between non-pointer types of differing alignment is well-defined behavior (it's semantically - /// equivalent to a memcpy). This lint has thus been refactored into two separate lints: - /// cast_ptr_alignment and transmute_ptr_to_ptr. + ("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"), #[clippy::version = "pre 1.29.0"] - pub MISALIGNED_TRANSMUTE, - "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint is too subjective, not having a good reason for being in clippy. - /// Additionally, compound assignment operators may be overloaded separately from their non-assigning - /// counterparts, so this lint may suggest a change in behavior or the code may not compile. + ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), #[clippy::version = "1.30.0"] - pub ASSIGN_OPS, - "using compound assignment operators (e.g., `+=`) is harmless" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// The original rule will only lint for `if let`. After - /// making it support to lint `match`, naming as `if let` is not suitable for it. - /// So, this lint is deprecated. + ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] - pub IF_LET_REDUNDANT_PATTERN_MATCHING, - "this lint has been changed to redundant_pattern_matching" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint used to suggest replacing `let mut vec = - /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The - /// replacement has very different performance characteristics so the lint is - /// deprecated. - #[clippy::version = "pre 1.29.0"] - pub UNSAFE_VECTOR_INITIALIZATION, - "the replacement suggested by this lint had substantially different behavior" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been superseded by #[must_use] in rustc. + ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), #[clippy::version = "1.39.0"] - pub UNUSED_COLLECT, - "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// Associated-constants are now preferred. + ("clippy::unused_collect", "`Iterator::collect` is now marked as `#[must_use]`"), #[clippy::version = "1.44.0"] - pub REPLACE_CONSTS, - "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// The regex! macro does not exist anymore. + ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), #[clippy::version = "1.47.0"] - pub REGEX_MACRO, - "the regex! macro has been removed from the regex crate in 2018" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been replaced by `manual_find_map`, a - /// more specific lint. - #[clippy::version = "1.51.0"] - pub FIND_MAP, - "this lint has been replaced by `manual_find_map`, a more specific lint" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been replaced by `manual_filter_map`, a - /// more specific lint. - #[clippy::version = "1.53.0"] - pub FILTER_MAP, - "this lint has been replaced by `manual_filter_map`, a more specific lint" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// The `avoid_breaking_exported_api` config option was added, which - /// enables the `enum_variant_names` lint for public items. + ("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"), #[clippy::version = "1.54.0"] - pub PUB_ENUM_VARIANT_NAMES, - "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// The `avoid_breaking_exported_api` config option was added, which - /// enables the `wrong_self_conversion` lint for public items. + ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), #[clippy::version = "1.54.0"] - pub WRONG_PUB_SELF_CONVENTION, - "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect the `#[cfg(features)]` and `#[cfg(tests)]` typos. - /// - /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs + ("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"), + // end deprecated lints. used by `cargo dev deprecate_lint` +]} + +#[rustfmt::skip] +declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ + #[clippy::version = ""] + ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), + #[clippy::version = ""] + ("clippy::blacklisted_name", "clippy::disallowed_names"), + #[clippy::version = ""] + ("clippy::block_in_if_condition_expr", "clippy::blocks_in_conditions"), + #[clippy::version = ""] + ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_conditions"), + #[clippy::version = ""] + ("clippy::blocks_in_if_conditions", "clippy::blocks_in_conditions"), + #[clippy::version = ""] + ("clippy::box_vec", "clippy::box_collection"), + #[clippy::version = ""] + ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), + #[clippy::version = ""] + ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), + #[clippy::version = ""] + ("clippy::derive_hash_xor_eq", "clippy::derived_hash_with_manual_eq"), + #[clippy::version = ""] + ("clippy::disallowed_method", "clippy::disallowed_methods"), + #[clippy::version = ""] + ("clippy::disallowed_type", "clippy::disallowed_types"), + #[clippy::version = ""] + ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), + #[clippy::version = "1.51.0"] + ("clippy::find_map", "clippy::manual_find_map"), + #[clippy::version = "1.53.0"] + ("clippy::filter_map", "clippy::manual_filter_map"), + #[clippy::version = ""] + ("clippy::identity_conversion", "clippy::useless_conversion"), + #[clippy::version = "pre 1.29.0"] + ("clippy::if_let_redundant_pattern_matching", "clippy::redundant_pattern_matching"), + #[clippy::version = ""] + ("clippy::if_let_some_result", "clippy::match_result_ok"), + #[clippy::version = ""] + ("clippy::incorrect_clone_impl_on_copy_type", "clippy::non_canonical_clone_impl"), + #[clippy::version = ""] + ("clippy::incorrect_partial_ord_impl_on_ord_type", "clippy::non_canonical_partial_ord_impl"), + #[clippy::version = ""] + ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), + #[clippy::version = ""] + ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), + #[clippy::version = ""] + ("clippy::new_without_default_derive", "clippy::new_without_default"), + #[clippy::version = ""] + ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), + #[clippy::version = ""] + ("clippy::option_expect_used", "clippy::expect_used"), + #[clippy::version = ""] + ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"), + #[clippy::version = ""] + ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"), + #[clippy::version = ""] + ("clippy::option_unwrap_used", "clippy::unwrap_used"), + #[clippy::version = ""] + ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"), + #[clippy::version = ""] + ("clippy::ref_in_deref", "clippy::needless_borrow"), + #[clippy::version = ""] + ("clippy::result_expect_used", "clippy::expect_used"), + #[clippy::version = ""] + ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"), + #[clippy::version = ""] + ("clippy::result_unwrap_used", "clippy::unwrap_used"), + #[clippy::version = ""] + ("clippy::single_char_push_str", "clippy::single_char_add_str"), + #[clippy::version = ""] + ("clippy::stutter", "clippy::module_name_repetitions"), + #[clippy::version = ""] + ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"), + #[clippy::version = ""] + ("clippy::to_string_in_display", "clippy::recursive_format_impl"), + #[clippy::version = ""] + ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), + #[clippy::version = ""] + ("clippy::zero_width_space", "clippy::invisible_characters"), + #[clippy::version = ""] + ("clippy::cast_ref_to_mut", "invalid_reference_casting"), + #[clippy::version = ""] + ("clippy::clone_double_ref", "suspicious_double_ref_op"), + #[clippy::version = ""] + ("clippy::cmp_nan", "invalid_nan_comparisons"), + #[clippy::version = ""] + ("clippy::drop_bounds", "drop_bounds"), + #[clippy::version = ""] + ("clippy::drop_copy", "dropping_copy_types"), + #[clippy::version = ""] + ("clippy::drop_ref", "dropping_references"), + #[clippy::version = ""] + ("clippy::fn_null_check", "useless_ptr_null_checks"), + #[clippy::version = ""] + ("clippy::for_loop_over_option", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loop_over_result", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::forget_copy", "forgetting_copy_types"), + #[clippy::version = ""] + ("clippy::forget_ref", "forgetting_references"), + #[clippy::version = ""] + ("clippy::into_iter_on_array", "array_into_iter"), + #[clippy::version = ""] + ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), + #[clippy::version = ""] + ("clippy::invalid_ref", "invalid_value"), + #[clippy::version = ""] + ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), + #[clippy::version = ""] + ("clippy::let_underscore_drop", "let_underscore_drop"), #[clippy::version = "1.80.0"] - pub MAYBE_MISUSED_CFG, - "this lint has been replaced by `unexpected_cfgs`" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect invalid `#[cfg(linux)]` attributes. - /// - /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs + ("clippy::maybe_misused_cfg", "unexpected_cfgs"), + #[clippy::version = ""] + ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), #[clippy::version = "1.80.0"] - pub MISMATCHED_TARGET_OS, - "this lint has been replaced by `unexpected_cfgs`" -} + ("clippy::mismatched_target_os", "unexpected_cfgs"), + #[clippy::version = ""] + ("clippy::panic_params", "non_fmt_panics"), + #[clippy::version = ""] + ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), + #[clippy::version = ""] + ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), + #[clippy::version = ""] + ("clippy::undropped_manually_drops", "undropped_manually_drops"), + #[clippy::version = ""] + ("clippy::unknown_clippy_lints", "unknown_lints"), + #[clippy::version = ""] + ("clippy::unused_label", "unused_labels"), + #[clippy::version = ""] + ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"), + #[clippy::version = ""] + ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), + // end renamed lints. used by `cargo dev rename_lint` +]} diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 253f9959e13e7..d0cb24884686a 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, is_manually_drop}; use clippy_utils::{ expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, DefinedTy, ExprUseNode, @@ -947,7 +947,7 @@ fn report<'tcx>( let (expr_str, _expr_is_macro_call) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let ty = typeck.expr_ty(expr); - let (_, ref_count) = peel_mid_ty_refs(ty); + let (_, ref_count) = peel_middle_ty_refs(ty); let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { // a deref call changing &T -> &U requires two deref operators the first time // this occurs. One to remove the reference, a second to call the deref impl. diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 5b6a5b08aa94c..d7b3a7c74f3ce 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::Visitable; -use clippy_utils::{in_constant, is_entrypoint_fn, is_trait_impl_item, method_chain_args}; +use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; use pulldown_cmark::Event::{ Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start, TaskListMarker, Text, @@ -768,7 +768,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize ); } }, - FootnoteReference(text) | Text(text) => { + Text(text) => { paragraph_range.end = range.end; let range_ = range.clone(); ticks_unbalanced |= text.contains('`') @@ -812,7 +812,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize } text_to_check.push((text, range, code_level)); } - }, + } + FootnoteReference(_) => {} } } headers @@ -857,7 +858,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { "assert" | "assert_eq" | "assert_ne" ) { - self.is_const = in_constant(self.cx, expr.hir_id); + self.is_const = self.cx.tcx.hir().is_inside_const_context(expr.hir_id); self.panic_span = Some(macro_call.span); } } diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 4a6ffcd9a7888..c7dd7292a14be 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_must_use_func_call; use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node}; @@ -126,14 +126,14 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { }, _ => return, }; - span_lint_and_note( - cx, - lint, - expr.span, - msg, - note_span, - format!("argument has type `{arg_ty}`"), - ); + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let note = format!("argument has type `{arg_ty}`"); + if let Some(span) = note_span { + diag.span_note(span, note); + } else { + diag.note(note); + } + }); } } } diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs index 7a6dc46972767..02f9c2c36488d 100644 --- a/clippy_lints/src/else_if_without_else.rs +++ b/clippy_lints/src/else_if_without_else.rs @@ -1,6 +1,6 @@ //! Lint on if expressions with an else if, but without a final else branch. -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -54,13 +54,15 @@ impl EarlyLintPass for ElseIfWithoutElse { && let ExprKind::If(_, _, None) = els.kind && !in_external_macro(cx.sess(), item.span) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, ELSE_IF_WITHOUT_ELSE, els.span, "`if` expression with an `else if`, but without a final `else`", - None, - "add an `else` block here", + |diag| { + diag.help("add an `else` block here"); + }, ); } } diff --git a/clippy_lints/src/empty_drop.rs b/clippy_lints/src/empty_drop.rs index c5fc72b5e2d8e..b66dd2108fc9b 100644 --- a/clippy_lints/src/empty_drop.rs +++ b/clippy_lints/src/empty_drop.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::peel_blocks; use rustc_errors::Applicability; use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node}; @@ -50,15 +50,14 @@ impl LateLintPass<'_> for EmptyDrop { && block.stmts.is_empty() && block.expr.is_none() { - span_lint_and_sugg( - cx, - EMPTY_DROP, - item.span, - "empty drop implementation", - "try removing this impl", - String::new(), - Applicability::MaybeIncorrect, - ); + span_lint_and_then(cx, EMPTY_DROP, item.span, "empty drop implementation", |diag| { + diag.span_suggestion_hidden( + item.span, + "try removing this impl", + String::new(), + Applicability::MaybeIncorrect, + ); + }); } } } diff --git a/clippy_lints/src/endian_bytes.rs b/clippy_lints/src/endian_bytes.rs index 5bba9c562b931..209104c5385c9 100644 --- a/clippy_lints/src/endian_bytes.rs +++ b/clippy_lints/src/endian_bytes.rs @@ -7,7 +7,6 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; use rustc_span::Symbol; -use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -141,52 +140,6 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix _ => return, }; - let mut help = None; - - 'build_help: { - // all lints disallowed, don't give help here - if [&[lint], other_lints.as_slice()] - .concat() - .iter() - .all(|lint| !lint.allowed(cx, expr)) - { - break 'build_help; - } - - // ne_bytes and all other lints allowed - if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) { - help = Some(Cow::Borrowed("specify the desired endianness explicitly")); - break 'build_help; - } - - // le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but - // le_bytes is not - if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) { - help = Some(Cow::Borrowed("use the native endianness instead")); - break 'build_help; - } - - let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr)); - let len = allowed_lints.clone().count(); - - let mut help_str = "use ".to_owned(); - - for (i, lint) in allowed_lints.enumerate() { - let only_one = len == 1; - if !only_one { - help_str.push_str("either of "); - } - - help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix))); - - if i != len && !only_one { - help_str.push_str("or "); - } - } - - help = Some(Cow::Owned(help_str + "instead")); - } - span_lint_and_then( cx, lint.as_lint(), @@ -198,9 +151,47 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix if prefix == Prefix::To { " method" } else { "" }, ), move |diag| { - if let Some(help) = help { - diag.help(help); + // all lints disallowed, don't give help here + if [&[lint], other_lints.as_slice()] + .concat() + .iter() + .all(|lint| !lint.allowed(cx, expr)) + { + return; + } + + // ne_bytes and all other lints allowed + if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) { + diag.help("specify the desired endianness explicitly"); + return; + } + + // le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but + // le_bytes is not + if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) { + diag.help("use the native endianness instead"); + return; + } + + let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr)); + let len = allowed_lints.clone().count(); + + let mut help_str = "use ".to_owned(); + + for (i, lint) in allowed_lints.enumerate() { + let only_one = len == 1; + if !only_one { + help_str.push_str("either of "); + } + + help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix))); + + if i != len && !only_one { + help_str.push_str("or "); + } } + help_str.push_str("instead"); + diag.help(help_str); }, ); } diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 30eb643c42ece..e54cd248ead29 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { .const_eval_poly(def_id.to_def_id()) .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty)); - if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx, c)) { + if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) { if let ty::Adt(adt, _) = ty.kind() { if adt.is_enum() { ty = adt.repr().discr_type().to_ty(cx.tcx); diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 0ed7859418bc5..5a7226d590c4d 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -9,8 +9,7 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ - self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, TyCtxt, - TypeVisitableExt, TypeckResults, + self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults, }; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; @@ -74,159 +73,184 @@ declare_clippy_lint! { declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]); impl<'tcx> LateLintPass<'tcx> for EtaReduction { - #[allow(clippy::too_many_lines)] - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let body = if let ExprKind::Closure(c) = expr.kind - && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) - && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_)) - && !expr.span.from_expansion() - { - cx.tcx.hir().body(c.body) - } else { - return; - }; - - if body.value.span.from_expansion() { - if body.params.is_empty() { - if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) { - // replace `|| vec![]` with `Vec::new` - span_lint_and_sugg( - cx, - REDUNDANT_CLOSURE, - expr.span, - "redundant closure", - "replace the closure with `Vec::new`", - "std::vec::Vec::new".into(), - Applicability::MachineApplicable, - ); - } + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::MethodCall(_method, receiver, args, _) = expr.kind { + for arg in args { + check_clousure(cx, Some(receiver), arg); } - // skip `foo(|| macro!())` - return; } + if let ExprKind::Call(func, args) = expr.kind { + check_clousure(cx, None, func); + for arg in args { + check_clousure(cx, None, arg); + } + } + } +} - let typeck = cx.typeck_results(); - let closure = if let ty::Closure(_, closure_subs) = typeck.expr_ty(expr).kind() { - closure_subs.as_closure() - } else { - return; - }; +#[allow(clippy::too_many_lines)] +fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) { + let body = if let ExprKind::Closure(c) = expr.kind + && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) + && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_)) + && !expr.span.from_expansion() + { + cx.tcx.hir().body(c.body) + } else { + return; + }; - if is_adjusted(cx, body.value) { - return; + if body.value.span.from_expansion() { + if body.params.is_empty() { + if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) { + // replace `|| vec![]` with `Vec::new` + span_lint_and_sugg( + cx, + REDUNDANT_CLOSURE, + expr.span, + "redundant closure", + "replace the closure with `Vec::new`", + "std::vec::Vec::new".into(), + Applicability::MachineApplicable, + ); + } } + // skip `foo(|| macro!())` + return; + } - match body.value.kind { - ExprKind::Call(callee, args) - if matches!( - callee.kind, - ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..)) - ) => + if is_adjusted(cx, body.value) { + return; + } + + let typeck = cx.typeck_results(); + let closure = if let ty::Closure(_, closure_subs) = typeck.expr_ty(expr).kind() { + closure_subs.as_closure() + } else { + return; + }; + let closure_sig = cx.tcx.signature_unclosure(closure.sig(), Safety::Safe).skip_binder(); + match body.value.kind { + ExprKind::Call(callee, args) + if matches!( + callee.kind, + ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..)) + ) => + { + let callee_ty_raw = typeck.expr_ty(callee); + let callee_ty = callee_ty_raw.peel_refs(); + if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) + || !check_inputs(typeck, body.params, None, args) { - let callee_ty_raw = typeck.expr_ty(callee); - let callee_ty = callee_ty_raw.peel_refs(); - if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) - || !check_inputs(typeck, body.params, None, args) - { - return; - } - let callee_ty_adjusted = typeck - .expr_adjustments(callee) - .last() - .map_or(callee_ty, |a| a.target.peel_refs()); + return; + } + let callee_ty_adjusted = typeck + .expr_adjustments(callee) + .last() + .map_or(callee_ty, |a| a.target.peel_refs()); - let sig = match callee_ty_adjusted.kind() { - ty::FnDef(def, _) => { - // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location` - if cx.tcx.has_attr(*def, sym::track_caller) { - return; - } + let sig = match callee_ty_adjusted.kind() { + ty::FnDef(def, _) => { + // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location` + if cx.tcx.has_attr(*def, sym::track_caller) { + return; + } - cx.tcx.fn_sig(def).skip_binder().skip_binder() - }, - ty::FnPtr(sig) => sig.skip_binder(), - ty::Closure(_, subs) => cx - .tcx - .signature_unclosure(subs.as_closure().sig(), Safety::Safe) - .skip_binder(), - _ => { - if typeck.type_dependent_def_id(body.value.hir_id).is_some() - && let subs = typeck.node_args(body.value.hir_id) - && let output = typeck.expr_ty(body.value) - && let ty::Tuple(tys) = *subs.type_at(1).kind() - { - cx.tcx.mk_fn_sig(tys, output, false, Safety::Safe, Abi::Rust) - } else { - return; - } - }, - }; - if check_sig(cx, closure, sig) - && let generic_args = typeck.node_args(callee.hir_id) - // Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not - // `'static` unless `T: 'static`. The cast `T::f as fn()` will, however, result - // in a type which is `'static`. - // For now ignore all callee types which reference a type parameter. - && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_))) - { - span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { - if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if path_to_local(callee).map_or(false, |l| { - // FIXME: Do we really need this `local_used_in` check? - // Isn't it checking something like... `callee(callee)`? - // If somehow this check is needed, add some test for it, - // 'cuz currently nothing changes after deleting this check. - local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) - }) { - match cx.tcx.infer_ctxt().build().err_ctxt().type_implements_fn_trait( - cx.param_env, - Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ty::PredicatePolarity::Positive, - ) { - // Mutable closure is used after current expr; we cannot consume it. - Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), - Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { - snippet = format!("&{snippet}"); - }, - _ => (), - } + cx.tcx.fn_sig(def).skip_binder().skip_binder() + }, + ty::FnPtr(sig) => sig.skip_binder(), + ty::Closure(_, subs) => cx + .tcx + .signature_unclosure(subs.as_closure().sig(), Safety::Safe) + .skip_binder(), + _ => { + if typeck.type_dependent_def_id(body.value.hir_id).is_some() + && let subs = typeck.node_args(body.value.hir_id) + && let output = typeck.expr_ty(body.value) + && let ty::Tuple(tys) = *subs.type_at(1).kind() + { + cx.tcx.mk_fn_sig(tys, output, false, Safety::Safe, Abi::Rust) + } else { + return; + } + }, + }; + if let Some(outer) = outer_receiver + && ty_has_static(sig.output()) + && let generic_args = typeck.node_args(outer.hir_id) + // HACK: Given a closure in `T.method(|| f())`, where `fn f() -> U where U: 'static`, `T.method(f)` + // will succeed iff `T: 'static`. But the region of `T` is always erased by `typeck.expr_ty()` when + // T is a generic type. For example, return type of `Option<String>::as_deref()` is a generic. + // So we have a hack like this. + && generic_args.len() > 0 + { + return; + } + if check_sig(closure_sig, sig) + && let generic_args = typeck.node_args(callee.hir_id) + // Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not + // `'static` unless `T: 'static`. The cast `T::f as fn()` will, however, result + // in a type which is `'static`. + // For now ignore all callee types which reference a type parameter. + && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_))) + { + span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { + if let Some(mut snippet) = snippet_opt(cx, callee.span) { + if path_to_local(callee).map_or(false, |l| { + // FIXME: Do we really need this `local_used_in` check? + // Isn't it checking something like... `callee(callee)`? + // If somehow this check is needed, add some test for it, + // 'cuz currently nothing changes after deleting this check. + local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) + }) { + match cx.tcx.infer_ctxt().build().err_ctxt().type_implements_fn_trait( + cx.param_env, + Binder::bind_with_vars(callee_ty_adjusted, List::empty()), + ty::PredicatePolarity::Positive, + ) { + // Mutable closure is used after current expr; we cannot consume it. + Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), + Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { + snippet = format!("&{snippet}"); + }, + _ => (), } - diag.span_suggestion( - expr.span, - "replace the closure with the function itself", - snippet, - Applicability::MachineApplicable, - ); } - }); - } - }, - ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => { - if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id) - && !cx.tcx.has_attr(method_def_id, sym::track_caller) - && check_sig(cx, closure, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder()) - { - span_lint_and_then( - cx, - REDUNDANT_CLOSURE_FOR_METHOD_CALLS, - expr.span, - "redundant closure", - |diag| { - let args = typeck.node_args(body.value.hir_id); - let caller = self_.hir_id.owner.def_id; - let type_name = get_path_from_caller_to_method_type(cx.tcx, caller, method_def_id, args); - diag.span_suggestion( - expr.span, - "replace the closure with the method itself", - format!("{}::{}", type_name, path.ident.name), - Applicability::MachineApplicable, - ); - }, - ); - } - }, - _ => (), - } + diag.span_suggestion( + expr.span, + "replace the closure with the function itself", + snippet, + Applicability::MachineApplicable, + ); + } + }); + } + }, + ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => { + if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id) + && !cx.tcx.has_attr(method_def_id, sym::track_caller) + && check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder()) + { + span_lint_and_then( + cx, + REDUNDANT_CLOSURE_FOR_METHOD_CALLS, + expr.span, + "redundant closure", + |diag| { + let args = typeck.node_args(body.value.hir_id); + let caller = self_.hir_id.owner.def_id; + let type_name = get_path_from_caller_to_method_type(cx.tcx, caller, method_def_id, args); + diag.span_suggestion( + expr.span, + "replace the closure with the method itself", + format!("{}::{}", type_name, path.ident.name), + Applicability::MachineApplicable, + ); + }, + ); + } + }, + _ => (), } } @@ -251,12 +275,8 @@ fn check_inputs( }) } -fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<TyCtxt<'tcx>>, call_sig: FnSig<'_>) -> bool { - call_sig.safety == Safety::Safe - && !has_late_bound_to_non_late_bound_regions( - cx.tcx.signature_unclosure(closure.sig(), Safety::Safe).skip_binder(), - call_sig, - ) +fn check_sig<'tcx>(closure_sig: FnSig<'tcx>, call_sig: FnSig<'tcx>) -> bool { + call_sig.safety == Safety::Safe && !has_late_bound_to_non_late_bound_regions(closure_sig, call_sig) } /// This walks through both signatures and checks for any time a late-bound region is expected by an @@ -265,7 +285,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<TyCtxt<'tcx>>, c /// This is needed because rustc is unable to late bind early-bound regions in a function signature. fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'_>) -> bool { fn check_region(from_region: Region<'_>, to_region: Region<'_>) -> bool { - matches!(from_region.kind(), RegionKind::ReBound(..)) && !matches!(to_region.kind(), RegionKind::ReBound(..)) + from_region.is_bound() && !to_region.is_bound() } fn check_subs(from_subs: &[GenericArg<'_>], to_subs: &[GenericArg<'_>]) -> bool { @@ -318,3 +338,8 @@ fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<' .zip(to_sig.inputs_and_output) .any(|(from_ty, to_ty)| check_ty(from_ty, to_ty)) } + +fn ty_has_static(ty: Ty<'_>) -> bool { + ty.walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if re.is_static())) +} diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 0f4176ec73bb2..9bf3baba4b597 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -88,11 +88,11 @@ impl LateLintPass<'_> for ExhaustiveItems { && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) && fields.iter().all(|f| cx.tcx.visibility(f.def_id).is_public()) { - let suggestion_span = item.span.shrink_to_lo(); - let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); span_lint_and_then(cx, lint, item.span, msg, |diag| { + let suggestion_span = item.span.shrink_to_lo(); + let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); let sugg = format!("#[non_exhaustive]\n{indent}"); - diag.span_suggestion( + diag.span_suggestion_verbose( suggestion_span, "try adding #[non_exhaustive]", sugg, diff --git a/clippy_lints/src/field_scoped_visibility_modifiers.rs b/clippy_lints/src/field_scoped_visibility_modifiers.rs index bb74e345703ff..95b8e882da792 100644 --- a/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -62,13 +62,15 @@ impl EarlyLintPass for FieldScopedVisibilityModifiers { // pub(self) is equivalent to not using pub at all, so we ignore it continue; } - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, FIELD_SCOPED_VISIBILITY_MODIFIERS, field.vis.span, "scoped visibility modifier on a field", - None, - "consider making the field private and adding a scoped visibility method for it", + |diag| { + diag.help("consider making the field private and adding a scoped visibility method for it"); + }, ); } } diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 6adcd2235dc52..f095c1add91f8 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -1,7 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal; use rustc_ast::ast::{self, LitFloatType, LitKind}; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, FloatTy}; @@ -105,32 +105,43 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { if is_whole && !sym_str.contains(['e', 'E']) { // Normalize the literal by stripping the fractional portion if sym_str.split('.').next().unwrap() != float_str { - // If the type suffix is missing the suggestion would be - // incorrectly interpreted as an integer so adding a `.0` - // suffix to prevent that. - if type_suffix.is_none() { - float_str.push_str(".0"); - } - - span_lint_and_sugg( + span_lint_and_then( cx, LOSSY_FLOAT_LITERAL, expr.span, "literal cannot be represented as the underlying type without loss of precision", - "consider changing the type or replacing it with", - numeric_literal::format(&float_str, type_suffix, true), - Applicability::MachineApplicable, + |diag| { + // If the type suffix is missing the suggestion would be + // incorrectly interpreted as an integer so adding a `.0` + // suffix to prevent that. + if type_suffix.is_none() { + float_str.push_str(".0"); + } + diag.span_suggestion_with_style( + expr.span, + "consider changing the type or replacing it with", + numeric_literal::format(&float_str, type_suffix, true), + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + }, ); } } else if digits > max as usize && float_str.len() < sym_str.len() { - span_lint_and_sugg( + span_lint_and_then( cx, EXCESSIVE_PRECISION, expr.span, "float has excessive precision", - "consider changing the type or truncating it to", - numeric_literal::format(&float_str, type_suffix, true), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion_with_style( + expr.span, + "consider changing the type or truncating it to", + numeric_literal::format(&float_str, type_suffix, true), + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + }, ); } } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 68bdf88d0a7e4..bf4bcabfe8911 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -1,9 +1,9 @@ use clippy_utils::consts::Constant::{Int, F32, F64}; -use clippy_utils::consts::{constant, constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ - eq_expr_value, get_parent_expr, higher, in_constant, is_inherent_method_call, is_no_std_crate, numeric_literal, - peel_blocks, sugg, + eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate, + numeric_literal, peel_blocks, sugg, }; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -112,7 +112,7 @@ declare_lint_pass!(FloatingPointArithmetic => [ // Returns the specialized log method for a given base if base is constant // and is one of 2, 10 and e fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> { - if let Some(value) = constant(cx, cx.typeck_results(), base) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(base) { if F32(2.0) == value || F64(2.0) == value { return Some("log2"); } else if F32(10.0) == value || F64(10.0) == value { @@ -182,10 +182,8 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { rhs, ) = receiver.kind { - let recv = match ( - constant(cx, cx.typeck_results(), lhs), - constant(cx, cx.typeck_results(), rhs), - ) { + let ecx = ConstEvalCtxt::new(cx); + let recv = match (ecx.eval(lhs), ecx.eval(rhs)) { (Some(value), _) if F32(1.0) == value || F64(1.0) == value => rhs, (_, Some(value)) if F32(1.0) == value || F64(1.0) == value => lhs, _ => return, @@ -230,7 +228,7 @@ fn get_integer_from_float_constant(value: &Constant<'_>) -> Option<i32> { fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { // Check receiver - if let Some(value) = constant(cx, cx.typeck_results(), receiver) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) { if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { Some("exp") } else if F32(2.0) == value || F64(2.0) == value { @@ -251,7 +249,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } // Check argument - if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) { let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value { ( SUBOPTIMAL_FLOPS, @@ -291,7 +289,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) { if value == Int(2) { if let Some(parent) = get_parent_expr(cx, expr) { if let Some(grandparent) = get_parent_expr(cx, parent) { @@ -397,8 +395,9 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> { ) = &add_rhs.kind && lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi" - && let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1) - && let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(lvalue) = ecx.eval(largs_1) + && let Some(rvalue) = ecx.eval(rargs_1) && Int(2) == lvalue && Int(2) == rvalue { @@ -438,7 +437,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = expr.kind && cx.typeck_results().expr_ty(lhs).is_floating_point() - && let Some(value) = constant(cx, cx.typeck_results(), rhs) + && let Some(value) = ConstEvalCtxt::new(cx).eval(rhs) && (F32(1.0) == value || F64(1.0) == value) && let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind && cx.typeck_results().expr_ty(self_arg).is_floating_point() @@ -552,7 +551,7 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - /// Returns true iff expr is some zero literal fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match constant_simple(cx, cx.typeck_results(), expr) { + match ConstEvalCtxt::new(cx).eval_simple(expr) { Some(Int(i)) => i == 0, Some(F32(f)) => f == 0.0, Some(F64(f)) => f == 0.0, @@ -696,8 +695,9 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { mul_lhs, mul_rhs, ) = &div_lhs.kind - && let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs) - && let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(rvalue) = ecx.eval(div_rhs) + && let Some(lvalue) = ecx.eval(mul_rhs) { // TODO: also check for constant values near PI/180 or 180/PI if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) @@ -753,7 +753,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // All of these operations are currently not const and are in std. - if in_constant(cx, expr.hir_id) { + if is_in_const_context(cx) { return; } diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs index a75538dd329b5..d05c5a01f41ce 100644 --- a/clippy_lints/src/format_push_string.rs +++ b/clippy_lints/src/format_push_string.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{higher, match_def_path, paths}; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource}; @@ -81,13 +81,15 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString { _ => return, }; if is_format(cx, arg) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, FORMAT_PUSH_STRING, expr.span, "`format!(..)` appended to existing `String`", - None, - "consider using `write!` to avoid the extra allocation", + |diag| { + diag.help("consider using `write!` to avoid the extra allocation"); + }, ); } } diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 9acb72b2e3729..6ab7bbc2dfc0c 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{in_constant, is_integer_literal}; +use clippy_utils::{is_in_const_context, is_integer_literal}; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -62,8 +62,8 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { && matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)) // do not lint in constant context, because the suggestion won't work. - // NB: keep this check until a new `const_trait_impl` is available and stablized. - && !in_constant(cx, exp.hir_id) + // NB: keep this check until a new `const_trait_impl` is available and stabilized. + && !is_in_const_context(cx) { let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { let ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index b38cc7b36a123..1c52514a330d4 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{higher, SpanlessEq}; +use clippy_utils::{eq_expr_value, higher}; use core::ops::ControlFlow; use rustc_errors::Diag; use rustc_hir::{Expr, ExprKind}; @@ -51,53 +51,45 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { if_else: Some(if_else), .. }) = higher::IfLet::hir(cx, expr) + && let Some(op_mutex) = for_each_expr_without_closures(let_expr, |e| mutex_lock_call(cx, e, None)) + && let Some(arm_mutex) = + for_each_expr_without_closures((if_then, if_else), |e| mutex_lock_call(cx, e, Some(op_mutex))) { - let is_mutex_lock = |e: &'tcx Expr<'tcx>| { - if let Some(mutex) = is_mutex_lock_call(cx, e) { - ControlFlow::Break(mutex) - } else { - ControlFlow::Continue(()) - } + let diag = |diag: &mut Diag<'_, ()>| { + diag.span_label( + op_mutex.span, + "this Mutex will remain locked for the entire `if let`-block...", + ); + diag.span_label( + arm_mutex.span, + "... and is tried to lock again here, which will always deadlock.", + ); + diag.help("move the lock call outside of the `if let ...` expression"); }; - - let op_mutex = for_each_expr_without_closures(let_expr, is_mutex_lock); - if let Some(op_mutex) = op_mutex { - let arm_mutex = for_each_expr_without_closures((if_then, if_else), is_mutex_lock); - if let Some(arm_mutex) = arm_mutex - && SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex) - { - let diag = |diag: &mut Diag<'_, ()>| { - diag.span_label( - op_mutex.span, - "this Mutex will remain locked for the entire `if let`-block...", - ); - diag.span_label( - arm_mutex.span, - "... and is tried to lock again here, which will always deadlock.", - ); - diag.help("move the lock call outside of the `if let ...` expression"); - }; - span_lint_and_then( - cx, - IF_LET_MUTEX, - expr.span, - "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock", - diag, - ); - } - } + span_lint_and_then( + cx, + IF_LET_MUTEX, + expr.span, + "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock", + diag, + ); } } } -fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +fn mutex_lock_call<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op_mutex: Option<&'tcx Expr<'_>>, +) -> ControlFlow<&'tcx Expr<'tcx>> { if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind && path.ident.as_str() == "lock" && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() && is_type_diagnostic_item(cx, ty, sym::Mutex) + && op_mutex.map_or(true, |op| eq_expr_value(cx, self_arg, op)) { - Some(self_arg) + ControlFlow::Break(self_arg) } else { - None + ControlFlow::Continue(()) } } diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index 2f6daeeb90d9d..0ebd8d0c237b6 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -1,7 +1,7 @@ //! lint on if branches that could be swapped so no `!` operation is necessary //! on the condition -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_else_clause; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; @@ -49,7 +49,7 @@ declare_clippy_lint! { declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]); fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { - if let Some(value) = constant_simple(cx, cx.typeck_results(), expr) { + if let Some(value) = ConstEvalCtxt::new(cx).eval_simple(expr) { return Constant::Int(0) == value; } false diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 39ea16b05d1ad..0bca53c1536de 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,10 +1,12 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; -use clippy_utils::{contains_return, higher, in_constant, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; +use clippy_utils::{ + contains_return, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, path_res, peel_blocks, +}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -76,37 +78,44 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) && !is_else_clause(cx.tcx, expr) - && !in_constant(cx, expr.hir_id) + && !is_in_const_context(cx) && !in_external_macro(cx.sess(), expr.span) && self.msrv.meets(msrvs::BOOL_THEN) && !contains_return(then_block.stmts) { - let mut app = Applicability::Unspecified; - let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) - .maybe_par() - .to_string(); - let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; - let mut method_body = if then_block.stmts.is_empty() { - arg_snip.into_owned() - } else { - format!("{{ /* snippet */ {arg_snip} }}") - }; let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) { "then_some" } else { - method_body.insert_str(0, "|| "); "then" }; - let help = - format!("consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`",); - span_lint_and_help( + span_lint_and_then( cx, IF_THEN_SOME_ELSE_NONE, expr.span, format!("this could be simplified with `bool::{method_name}`"), - None, - help, + |diag| { + let mut app = Applicability::Unspecified; + let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) + .maybe_par() + .to_string(); + let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; + let method_body = if let Some(first_stmt) = then_block.stmts.first() { + let (block_snippet, _) = + snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app); + let closure = if method_name == "then" { "|| " } else { "" }; + format!("{closure} {{ {block_snippet}; {arg_snip} }}") + } else { + arg_snip.into_owned() + }; + + diag.span_suggestion( + expr.span, + "try", + format!("{cond_snip}.{method_name}({method_body})"), + app, + ); + }, ); } } diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 344a04e6e7e82..e56f33f8dcfe2 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; -use rustc_errors::Diag; +use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; @@ -13,7 +13,7 @@ use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; use rustc_span::Span; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; @@ -77,33 +77,32 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { &generics_snip[1..generics_snip.len() - 1] }; - multispan_sugg( - diag, - "consider adding a type parameter", - vec![ - ( - generics_suggestion_span, - format!( - "<{generics_snip}{}S: ::std::hash::BuildHasher{}>", - if generics_snip.is_empty() { "" } else { ", " }, - if vis.suggestions.is_empty() { - "" - } else { - // request users to add `Default` bound so that generic constructors can be used - " + Default" - }, - ), - ), - ( - target.span(), - format!("{}<{}, S>", target.type_name(), target.type_arguments(),), + let mut suggestions = vec![ + ( + generics_suggestion_span, + format!( + "<{generics_snip}{}S: ::std::hash::BuildHasher{}>", + if generics_snip.is_empty() { "" } else { ", " }, + if vis.suggestions.is_empty() { + "" + } else { + // request users to add `Default` bound so that generic constructors can be used + " + Default" + }, ), - ], + ), + ( + target.span(), + format!("{}<{}, S>", target.type_name(), target.type_arguments(),), + ), + ]; + suggestions.extend(vis.suggestions); + + diag.multipart_suggestion( + "add a type parameter for `BuildHasher`", + suggestions, + Applicability::MaybeIncorrect, ); - - if !vis.suggestions.is_empty() { - multispan_sugg(diag, "...and use generic constructor", vis.suggestions); - } } if !cx.effective_visibilities.is_exported(item.owner_id.def_id) { diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index a102b434cfabb..b926e1e62ba0c 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context, wal use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro}; use core::ops::ControlFlow; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::intravisit::FnKind; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -45,8 +45,6 @@ declare_clippy_lint! { declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]); fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, span, "..", &mut app); span_lint_hir_and_then( cx, IMPLICIT_RETURN, @@ -54,14 +52,20 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) { span, "missing `return` statement", |diag| { - diag.span_suggestion(span, "add `return` as shown", format!("return {snip}"), app); + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, span, "..", &mut app); + diag.span_suggestion_with_style( + span, + "add `return` as shown", + format!("return {snip}"), + app, + SuggestionStyle::ShowAlways, + ); }, ); } fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, expr_span: Span) { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0; span_lint_hir_and_then( cx, IMPLICIT_RETURN, @@ -69,11 +73,14 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp break_span, "missing `return` statement", |diag| { - diag.span_suggestion( + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0; + diag.span_suggestion_with_style( break_span, "change `break` to `return` as shown", format!("return {snip}"), app, + SuggestionStyle::ShowAlways, ); }, ); diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index f225c6e7f049f..dd5908553e59b 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; @@ -117,11 +117,11 @@ fn get_int_max(ty: Ty<'_>) -> Option<u128> { fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { - let tr = cx.typeck_results(); - if let Some(Constant::Int(c)) = constant(cx, tr, r) { + let ecx = ConstEvalCtxt::new(cx); + if let Some(Constant::Int(c)) = ecx.eval(r) { return Some((c, op.node, l)); }; - if let Some(Constant::Int(c)) = constant(cx, tr, l) { + if let Some(Constant::Int(c)) = ecx.eval(l) { return Some((c, invert_op(op.node)?, r)); } } diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 12ca6d43b27bc..0ef5b803a89d6 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -55,7 +55,6 @@ impl IncompatibleMsrv { } } - #[allow(clippy::cast_lossless)] fn get_def_id_version(&mut self, tcx: TyCtxt<'_>, def_id: DefId) -> RustcVersion { if let Some(version) = self.is_above_msrv.get(&def_id) { return *version; @@ -67,9 +66,9 @@ impl IncompatibleMsrv { since: StableSince::Version(version), .. } => Some(RustcVersion::new( - version.major as _, - version.minor as _, - version.patch as _, + version.major.into(), + version.minor.into(), + version.patch.into(), )), _ => None, }) { diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 526b4e1fba0e9..2f9661c9ea385 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; use clippy_utils::ty::is_copy; @@ -246,7 +246,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { && let parent_id = cx.tcx.parent_hir_id(expr.hir_id) && let hir::Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) && let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind - && let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr) + && let Some(Constant::Int(index_value)) = ConstEvalCtxt::new(cx).eval(index_expr) && let Ok(index_value) = index_value.try_into() && index_value < max_suggested_slice diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 6729c7c8d1018..3ac50b8f1fba5 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -1,7 +1,7 @@ //! lint on indexing and slicing operations use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; use clippy_utils::{higher, is_from_proc_macro}; @@ -70,8 +70,6 @@ declare_clippy_lint! { /// /// Use instead: /// ```no_run - /// # #![allow(unused)] - /// /// # let x = vec![0; 5]; /// # let y = [0, 1, 2, 3]; /// x.get(2); @@ -179,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { return; } // Index is a constant uint. - if let Some(constant) = constant(cx, cx.typeck_results(), index) { + if let Some(constant) = ConstEvalCtxt::new(cx).eval(index) { // only `usize` index is legal in rust array index // leave other type to rustc if let Constant::Int(off) = constant @@ -217,14 +215,15 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { /// Returns a tuple of options with the start and end (exclusive) values of /// the range. If the start or end is not constant, None is returned. fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option<u128>, Option<u128>) { - let s = range.start.map(|expr| constant(cx, cx.typeck_results(), expr)); + let ecx = ConstEvalCtxt::new(cx); + let s = range.start.map(|expr| ecx.eval(expr)); let start = match s { Some(Some(Constant::Int(x))) => Some(x), Some(_) => None, None => Some(0), }; - let e = range.end.map(|expr| constant(cx, cx.typeck_results(), expr)); + let e = range.end.map(|expr| ecx.eval(expr)); let end = match e { Some(Some(Constant::Int(x))) => { if range.limits == RangeLimits::Closed { diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index fa7e7f6b76d16..676d50c4951b1 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -41,7 +41,6 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// let infinite_iter = 0..; - /// # #[allow(unused)] /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5)); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 0d3786dad4b1e..9eed7aa924339 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -1,6 +1,6 @@ //! lint on inherent implementations -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; @@ -105,13 +105,14 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first. lint_spans.sort_by_key(|x| x.0.lo()); for (span, first_span) in lint_spans { - span_lint_and_note( + span_lint_and_then( cx, MULTIPLE_INHERENT_IMPL, span, "multiple implementations of this structure", - Some(first_span), - "first implementation here", + |diag| { + diag.span_note(first_span, "first implementation here"); + }, ); } } diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 30f2285bdd235..1929fbded3b56 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -7,7 +7,7 @@ use rustc_span::Span; use clippy_utils::comparisons; use clippy_utils::comparisons::Rel; -use clippy_utils::consts::{constant_full_int, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet; @@ -95,7 +95,7 @@ fn upcast_comparison_bounds_err<'tcx>( invert: bool, ) { if let Some((lb, ub)) = lhs_bounds { - if let Some(norm_rhs_val) = constant_full_int(cx, cx.typeck_results(), rhs) { + if let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) { if rel == Rel::Eq || rel == Rel::Ne { if norm_rhs_val < lb || norm_rhs_val > ub { err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index c67da689aaee0..f2f841dcec33d 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -66,16 +66,18 @@ impl LateLintPass<'_> for LargeIncludeFile { && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) { - span_lint_and_note( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LARGE_INCLUDE_FILE, expr.span.source_callsite(), "attempted to include a large file", - None, - format!( - "the configuration allows a maximum size of {} bytes", - self.max_file_size - ), + |diag| { + diag.note(format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + )); + }, ); } } diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 8fa63f3e8fde0..b522c22a44d70 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths}; use rustc_hir::{LetStmt, LocalSource, PatKind}; @@ -149,43 +149,53 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }); if contains_sync_guard { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LET_UNDERSCORE_LOCK, local.span, "non-binding `let` on a synchronization lock", - None, - "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`", + |diag| { + diag.help( + "consider using an underscore-prefixed named \ + binding or dropping explicitly with `std::mem::drop`", + ); + }, ); } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LET_UNDERSCORE_FUTURE, local.span, "non-binding `let` on a future", - None, - "consider awaiting the future or dropping explicitly with `std::mem::drop`", + |diag| { + diag.help("consider awaiting the future or dropping explicitly with `std::mem::drop`"); + }, ); } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LET_UNDERSCORE_MUST_USE, local.span, "non-binding `let` on an expression with `#[must_use]` type", - None, - "consider explicitly using expression value", + |diag| { + diag.help("consider explicitly using expression value"); + }, ); } else if is_must_use_func_call(cx, init) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LET_UNDERSCORE_MUST_USE, local.span, "non-binding `let` on a result of a `#[must_use]` function", - None, - "consider explicitly using function result", + |diag| { + diag.help("consider explicitly using function result"); + }, ); } @@ -204,18 +214,22 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { return; } - span_lint_and_help( + span_lint_and_then( cx, LET_UNDERSCORE_UNTYPED, local.span, "non-binding `let` without a type annotation", - Some(Span::new( - local.pat.span.hi(), - local.pat.span.hi() + BytePos(1), - local.pat.span.ctxt(), - local.pat.span.parent(), - )), - "consider adding a type annotation", + |diag| { + diag.span_help( + Span::new( + local.pat.span.hi(), + local.pat.span.hi() + BytePos(1), + local.pat.span.ctxt(), + local.pat.span.parent(), + ), + "consider adding a type annotation", + ); + }, ); } } diff --git a/clippy_lints/src/lib.deprecated.rs b/clippy_lints/src/lib.deprecated.rs deleted file mode 100644 index 0d21261822dd0..0000000000000 --- a/clippy_lints/src/lib.deprecated.rs +++ /dev/null @@ -1,78 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -{ - store.register_removed( - "clippy::should_assert_eq", - "`assert!()` will be more flexible with RFC 2011", - ); - store.register_removed( - "clippy::extend_from_slice", - "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice", - ); - store.register_removed( - "clippy::range_step_by_zero", - "`iterator.step_by(0)` panics nowadays", - ); - store.register_removed( - "clippy::unstable_as_slice", - "`Vec::as_slice` has been stabilized in 1.7", - ); - store.register_removed( - "clippy::unstable_as_mut_slice", - "`Vec::as_mut_slice` has been stabilized in 1.7", - ); - store.register_removed( - "clippy::misaligned_transmute", - "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", - ); - store.register_removed( - "clippy::assign_ops", - "using compound assignment operators (e.g., `+=`) is harmless", - ); - store.register_removed( - "clippy::if_let_redundant_pattern_matching", - "this lint has been changed to redundant_pattern_matching", - ); - store.register_removed( - "clippy::unsafe_vector_initialization", - "the replacement suggested by this lint had substantially different behavior", - ); - store.register_removed( - "clippy::unused_collect", - "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint", - ); - store.register_removed( - "clippy::replace_consts", - "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants", - ); - store.register_removed( - "clippy::regex_macro", - "the regex! macro has been removed from the regex crate in 2018", - ); - store.register_removed( - "clippy::find_map", - "this lint has been replaced by `manual_find_map`, a more specific lint", - ); - store.register_removed( - "clippy::filter_map", - "this lint has been replaced by `manual_filter_map`, a more specific lint", - ); - store.register_removed( - "clippy::pub_enum_variant_names", - "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items", - ); - store.register_removed( - "clippy::wrong_pub_self_convention", - "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items", - ); - store.register_removed( - "clippy::maybe_misused_cfg", - "this lint has been replaced by `unexpected_cfgs`", - ); - store.register_removed( - "clippy::mismatched_target_os", - "this lint has been replaced by `unexpected_cfgs`", - ); -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a388b6b2eaab3..ce13a9afef5b0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -13,7 +13,6 @@ #![feature(stmt_expr_attributes)] #![feature(unwrap_infallible)] #![recursion_limit = "512"] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow( clippy::missing_docs_in_private_items, clippy::must_use_candidate, @@ -64,13 +63,11 @@ extern crate clippy_utils; #[macro_use] extern crate declare_clippy_lint; -#[cfg(feature = "internal")] -pub mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; mod declared_lints; -mod renamed_lints; +mod deprecated_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absolute_paths; @@ -372,6 +369,7 @@ mod unsafe_removed_from_name; mod unused_async; mod unused_io_amount; mod unused_peekable; +mod unused_result_ok; mod unused_rounding; mod unused_self; mod unused_unit; @@ -495,7 +493,7 @@ pub fn explain(name: &str) -> i32 { // Check if the lint has configuration let mut mdconf = get_configuration_metadata(); let name = name.to_ascii_lowercase(); - mdconf.retain(|cconf| cconf.lints.contains(&name)); + mdconf.retain(|cconf| cconf.lints.contains(&&*name)); if !mdconf.is_empty() { println!("### Configuration for {}:\n", info.lint.name_lower()); for conf in mdconf { @@ -531,10 +529,14 @@ fn register_categories(store: &mut rustc_lint::LintStore) { /// Used in `./src/driver.rs`. #[expect(clippy::too_many_lines)] pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { - register_removed_non_tool_lints(store); register_categories(store); - include!("lib.deprecated.rs"); + for (old_name, new_name) in deprecated_lints::RENAMED { + store.register_renamed(old_name, new_name); + } + for (name, reason) in deprecated_lints::DEPRECATED { + store.register_removed(name, reason); + } #[cfg(feature = "internal")] { @@ -669,6 +671,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(conf))); store.register_late_pass(|_| Box::new(missing_inline::MissingInline)); store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems)); + store.register_late_pass(|_| Box::new(unused_result_ok::UnusedResultOk)); store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk)); store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)); store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount)); @@ -818,7 +821,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(conf))); store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); - store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default()); + store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))); store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); @@ -907,68 +910,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))); store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf))); store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)); - store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains)); + store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert)); store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice)); store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest)); // add lints here, do not remove this comment, it's used in `new_lint` } - -#[rustfmt::skip] -fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { - store.register_removed( - "should_assert_eq", - "`assert!()` will be more flexible with RFC 2011", - ); - store.register_removed( - "extend_from_slice", - "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice", - ); - store.register_removed( - "range_step_by_zero", - "`iterator.step_by(0)` panics nowadays", - ); - store.register_removed( - "unstable_as_slice", - "`Vec::as_slice` has been stabilized in 1.7", - ); - store.register_removed( - "unstable_as_mut_slice", - "`Vec::as_mut_slice` has been stabilized in 1.7", - ); - store.register_removed( - "misaligned_transmute", - "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", - ); - store.register_removed( - "assign_ops", - "using compound assignment operators (e.g., `+=`) is harmless", - ); - store.register_removed( - "if_let_redundant_pattern_matching", - "this lint has been changed to redundant_pattern_matching", - ); - store.register_removed( - "unsafe_vector_initialization", - "the replacement suggested by this lint had substantially different behavior", - ); - store.register_removed( - "reverse_range_loop", - "this lint is now included in reversed_empty_ranges", - ); -} - -/// Register renamed lints. -/// -/// Used in `./src/driver.rs`. -pub fn register_renamed(ls: &mut rustc_lint::LintStore) { - for (old_name, new_name) in renamed_lints::RENAMED_LINTS { - ls.register_renamed(old_name, new_name); - } -} - -// only exists to let the dogfood integration test works. -// Don't run clippy as an executable directly -#[allow(dead_code)] -fn main() { - panic!("Please use the cargo-clippy executable"); -} diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index b685d1dad1a75..259e4d6c08fb7 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -2,13 +2,13 @@ //! floating-point literal expressions. use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_ast::token; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -159,63 +159,39 @@ enum WarningType { } impl WarningType { - fn display(&self, suggested_format: String, cx: &EarlyContext<'_>, span: Span) { + fn lint_and_text(&self) -> (&'static Lint, &'static str, &'static str) { match self { - Self::MistypedLiteralSuffix => span_lint_and_sugg( - cx, + Self::MistypedLiteralSuffix => ( MISTYPED_LITERAL_SUFFIXES, - span, "mistyped literal suffix", "did you mean to write", - suggested_format, - Applicability::MaybeIncorrect, ), - Self::UnreadableLiteral => span_lint_and_sugg( - cx, - UNREADABLE_LITERAL, - span, - "long literal lacking separators", - "consider", - suggested_format, - Applicability::MachineApplicable, - ), - Self::LargeDigitGroups => span_lint_and_sugg( - cx, - LARGE_DIGIT_GROUPS, - span, - "digit groups should be smaller", - "consider", - suggested_format, - Applicability::MachineApplicable, - ), - Self::InconsistentDigitGrouping => span_lint_and_sugg( - cx, + Self::UnreadableLiteral => (UNREADABLE_LITERAL, "long literal lacking separators", "consider"), + Self::LargeDigitGroups => (LARGE_DIGIT_GROUPS, "digit groups should be smaller", "consider"), + Self::InconsistentDigitGrouping => ( INCONSISTENT_DIGIT_GROUPING, - span, "digits grouped inconsistently by underscores", "consider", - suggested_format, - Applicability::MachineApplicable, ), - Self::DecimalRepresentation => span_lint_and_sugg( - cx, + Self::DecimalRepresentation => ( DECIMAL_LITERAL_REPRESENTATION, - span, "integer literal has a better hexadecimal representation", "consider", - suggested_format, - Applicability::MachineApplicable, ), - Self::UnusualByteGroupings => span_lint_and_sugg( - cx, + Self::UnusualByteGroupings => ( UNUSUAL_BYTE_GROUPINGS, - span, "digits of hex, binary or octal literal not in groups of equal size", "consider", - suggested_format, - Applicability::MachineApplicable, ), - }; + } + } + + fn display(&self, num_lit: &NumericLiteral<'_>, cx: &EarlyContext<'_>, span: Span) { + let (lint, message, try_msg) = self.lint_and_text(); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, lint, span, message, |diag| { + diag.span_suggestion(span, try_msg, num_lit.format(), Applicability::MaybeIncorrect); + }); } } @@ -293,7 +269,7 @@ impl LiteralDigitGrouping { WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => true, }; if should_warn { - warning_type.display(num_lit.format(), cx, span); + warning_type.display(&num_lit, cx, span); } } } @@ -346,11 +322,14 @@ impl LiteralDigitGrouping { } } *part = main_part; - let mut sugg = num_lit.format(); - sugg.push('_'); - sugg.push(missing_char); - sugg.push_str(last_group); - WarningType::MistypedLiteralSuffix.display(sugg, cx, span); + let (lint, message, try_msg) = WarningType::MistypedLiteralSuffix.lint_and_text(); + span_lint_and_then(cx, lint, span, message, |diag| { + let mut sugg = num_lit.format(); + sugg.push('_'); + sugg.push(missing_char); + sugg.push_str(last_group); + diag.span_suggestion(span, try_msg, sugg, Applicability::MaybeIncorrect); + }); false } else { true @@ -471,7 +450,7 @@ impl DecimalLiteralRepresentation { let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let _: Result<(), ()> = Self::do_lint(num_lit.integer).map_err(|warning_type| { - warning_type.display(num_lit.format(), cx, span); + warning_type.display(&num_lit, cx, span); }); } } diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index f0ee64d714e05..73a23615c2dc7 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -2,6 +2,7 @@ use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{get_enclosing_block, is_integer_const}; +use rustc_ast::Label; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr}; use rustc_hir::{Expr, Pat}; @@ -17,6 +18,7 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, + label: Option<Label>, ) { // Look for variables that are incremented once per loop iteration. let mut increment_visitor = IncrementVisitor::new(cx); @@ -34,7 +36,7 @@ pub(super) fn check<'tcx>( { let mut applicability = Applicability::MaybeIncorrect; let span = expr.span.with_hi(arg.span.hi()); - + let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name)); let int_name = match ty.map(Ty::kind) { // usize or inferred Some(ty::Uint(UintTy::Usize)) | None => { @@ -45,7 +47,7 @@ pub(super) fn check<'tcx>( format!("the variable `{name}` is used as a loop counter"), "consider using", format!( - "for ({name}, {}) in {}.enumerate()", + "{loop_label}for ({name}, {}) in {}.enumerate()", snippet_with_applicability(cx, pat.span, "item", &mut applicability), make_iterator_snippet(cx, arg, &mut applicability), ), @@ -68,7 +70,7 @@ pub(super) fn check<'tcx>( span, "consider using", format!( - "for ({name}, {}) in (0_{int_name}..).zip({})", + "{loop_label}for ({name}, {}) in (0_{int_name}..).zip({})", snippet_with_applicability(cx, pat.span, "item", &mut applicability), make_iterator_snippet(cx, arg, &mut applicability), ), diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index 6922533fbe9d3..185d834becafc 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -1,8 +1,9 @@ use super::FOR_KV_MAP; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; +use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -40,13 +41,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx format!("you seem to want to iterate on a map's {kind}s"), |diag| { let map = sugg::Sugg::hir(cx, arg, "map"); - multispan_sugg( - diag, + diag.multipart_suggestion( "use the corresponding method", vec![ (pat_span, snippet(cx, new_pat_span, kind).into_owned()), (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_par())), ], + Applicability::MachineApplicable, ); }, ); diff --git a/clippy_lints/src/loops/manual_while_let_some.rs b/clippy_lints/src/loops/manual_while_let_some.rs index b00a082bb8cf9..57434f3554443 100644 --- a/clippy_lints/src/loops/manual_while_let_some.rs +++ b/clippy_lints/src/loops/manual_while_let_some.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::{match_def_path, paths, SpanlessEq}; use rustc_errors::Applicability; @@ -38,11 +38,10 @@ fn report_lint(cx: &LateContext<'_>, pop_span: Span, pop_stmt_kind: PopStmt<'_>, }; let loop_replacement = format!("while let Some({}) = {}.pop()", pat, snippet(cx, receiver_span, "..")); - multispan_sugg_with_applicability( - diag, + diag.multipart_suggestion( "consider using a `while..let` loop", + vec![(loop_span, loop_replacement), (pop_span, pop_replacement)], Applicability::MachineApplicable, - [(loop_span, loop_replacement), (pop_span, pop_replacement)], ); }, ); diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 7c2a8098af287..92ccc0cc0a15e 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -25,6 +25,7 @@ mod while_let_on_iterator; use clippy_config::msrvs::Msrv; use clippy_config::Conf; use clippy_utils::higher; +use rustc_ast::Label; use rustc_hir::{Expr, ExprKind, LoopSource, Pat}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -448,7 +449,7 @@ declare_clippy_lint! { #[clippy::version = "1.80.0"] pub WHILE_FLOAT, nursery, - "while loops comaparing floating point values" + "while loops comparing floating point values" } declare_clippy_lint! { @@ -760,6 +761,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { body, loop_id, span, + label, }) = for_loop { // we don't want to check expanded macros @@ -768,7 +770,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { if body.span.from_expansion() { return; } - self.check_for_loop(cx, pat, arg, body, expr, span); + self.check_for_loop(cx, pat, arg, body, expr, span, label); if let ExprKind::Block(block, _) = body.kind { never_loop::check(cx, block, loop_id, span, for_loop.as_ref()); } @@ -808,6 +810,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { } impl Loops { + #[allow(clippy::too_many_arguments)] fn check_for_loop<'tcx>( &self, cx: &LateContext<'tcx>, @@ -816,11 +819,12 @@ impl Loops { body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, span: Span, + label: Option<Label>, ) { let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr); if !is_manual_memcpy_triggered { needless_range_loop::check(cx, pat, arg, body, expr); - explicit_counter_loop::check(cx, pat, arg, body, expr); + explicit_counter_loop::check(cx, pat, arg, body, expr, label); } self.check_for_loop_arg(cx, pat, arg); for_kv_map::check(cx, pat, arg, body); diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index de7ec81bc0108..e18e4374667de 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -1,11 +1,12 @@ use super::NEEDLESS_RANGE_LOOP; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::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, sugg, SpanlessEq}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{BinOpKind, BorrowKind, Closure, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath}; @@ -145,8 +146,7 @@ pub(super) fn check<'tcx>( arg.span, format!("the loop variable `{}` is used to index `{indexed}`", ident.name), |diag| { - multispan_sugg( - diag, + diag.multipart_suggestion( "consider using an iterator and enumerate()", vec![ (pat.span, format!("({}, <item>)", ident.name)), @@ -155,6 +155,7 @@ pub(super) fn check<'tcx>( format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), ), ], + Applicability::HasPlaceholders, ); }, ); @@ -171,10 +172,10 @@ pub(super) fn check<'tcx>( arg.span, format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), |diag| { - multispan_sugg( - diag, + diag.multipart_suggestion( "consider using an iterator", vec![(pat.span, "<item>".to_string()), (arg.span, repl)], + Applicability::HasPlaceholders, ); }, ); diff --git a/clippy_lints/src/loops/unused_enumerate_index.rs b/clippy_lints/src/loops/unused_enumerate_index.rs index 40ccfec02be58..51e21aa9734ed 100644 --- a/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,7 +1,8 @@ use super::UNUSED_ENUMERATE_INDEX; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::{pat_is_wild, sugg}; +use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; @@ -28,13 +29,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_ "you seem to use `.enumerate()` and immediately discard the index", |diag| { let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter"); - multispan_sugg( - diag, + diag.multipart_suggestion( "remove the `.enumerate()` call", vec![ (pat.span, snippet(cx, elem.span, "..").into_owned()), (arg.span, base_iter.to_string()), ], + Applicability::MachineApplicable, ); }, ); diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index e7b3a2c4973cf..cc1bd5929d0fc 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -1,5 +1,5 @@ use super::WHILE_IMMUTABLE_CONDITION; -use clippy_utils::consts::constant; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::usage::mutated_variables; use rustc_hir::def::{DefKind, Res}; @@ -10,7 +10,7 @@ use rustc_lint::LateContext; use std::ops::ControlFlow; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { - if constant(cx, cx.typeck_results(), cond).is_some() { + if ConstEvalCtxt::new(cx).eval(cond).is_some() { // A pure constant condition (e.g., `while false`) is not linted. return; } diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 194dd4752f91b..c171fa1c622af 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -14,7 +14,7 @@ use rustc_span::symbol::sym; use rustc_span::Symbol; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(higher::WhileLet { if_then, let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) + if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr) // check for `Some(..)` pattern && let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) @@ -27,6 +27,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { && !uses_iter(cx, &iter_expr_struct, if_then) { let mut applicability = Applicability::MachineApplicable; + + let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name)); + let loop_var = if let Some(some_pat) = some_pat.first() { if is_refutable(cx, some_pat) { // Refutable patterns don't work with for loops. @@ -57,7 +60,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { expr.span.with_hi(let_expr.span.hi()), "this loop could be written as a `for` loop", "try", - format!("for {loop_var} in {iterator}{by_ref}"), + format!("{loop_label}for {loop_var} in {iterator}{by_ref}"), applicability, ); } diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index a79ad018a042f..4123c933660bc 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -1,13 +1,13 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; use clippy_utils::{ - eq_expr_value, in_constant, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, + eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt, MaybePath, }; use itertools::Itertools; @@ -122,8 +122,9 @@ impl<'tcx> ClampSuggestion<'tcx> { if max_type != min_type { return false; } - if let Some(max) = constant(cx, cx.typeck_results(), self.params.max) - && let Some(min) = constant(cx, cx.typeck_results(), self.params.min) + let ecx = ConstEvalCtxt::new(cx); + if let Some(max) = ecx.eval(self.params.max) + && let Some(min) = ecx.eval(self.params.min) && let Some(ord) = Constant::partial_cmp(cx.tcx, max_type, &min, &max) { ord != Ordering::Greater @@ -146,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { if !self.msrv.meets(msrvs::CLAMP) { return; } - if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) { + if !expr.span.from_expansion() && !is_in_const_context(cx) { let suggestion = is_if_elseif_else_pattern(cx, expr) .or_else(|| is_max_min_pattern(cx, expr)) .or_else(|| is_call_max_min_pattern(cx, expr)) @@ -159,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { } fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if !self.msrv.meets(msrvs::CLAMP) || in_constant(cx, block.hir_id) { + if !self.msrv.meets(msrvs::CLAMP) || is_in_const_context(cx) { return; } for suggestion in is_two_if_pattern(cx, block) { diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 03416ba96de73..6bdc79129a9bc 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::{is_from_proc_macro, path_to_local}; @@ -95,8 +95,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { || cx.tcx.features().declared(sym!(const_float_classify)) ) && let [first, second, const_1, const_2] = exprs - && let Some(const_1) = constant(cx, cx.typeck_results(), const_1) - && let Some(const_2) = constant(cx, cx.typeck_results(), const_2) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(const_1) = ecx.eval(const_1) + && let Some(const_2) = ecx.eval(const_2) && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in // case somebody does that for some reason diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index a9f21d34e4cee..31c37c3bc3b14 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -3,7 +3,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; -use clippy_utils::{higher, in_constant, path_to_local, peel_ref_operators}; +use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators}; use rustc_ast::ast::RangeLimits; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { return; } - if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) { + if is_in_const_context(cx) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) { return; } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 78a750f0dcd04..6cf5d272d7d82 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -1,9 +1,9 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant_full_int, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::{in_constant, path_to_local}; +use clippy_utils::{is_in_const_context, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && add_rhs.span.ctxt() == ctxt && !in_external_macro(cx.sess(), expr.span) && self.msrv.meets(msrvs::REM_EUCLID) - && (self.msrv.meets(msrvs::REM_EUCLID_CONST) || !in_constant(cx, expr.hir_id)) + && (self.msrv.meets(msrvs::REM_EUCLID_CONST) || !is_in_const_context(cx)) && let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs) && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs) && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind @@ -117,7 +117,7 @@ fn check_for_either_unsigned_int_constant<'a>( } fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<u128> { - let int_const = constant_full_int(cx, cx.typeck_results(), expr)?; + let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr)?; match int_const { FullInt::S(s) => s.try_into().ok(), FullInt::U(u) => Some(u), diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index a517a4d507526..07537fc65c08c 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg; use rustc_errors::Applicability; @@ -66,7 +66,7 @@ fn parse_shift<'tcx>( BinOpKind::Shr => ShiftDirection::Right, _ => return None, }; - let const_expr = constant(cx, cx.typeck_results(), r)?; + let const_expr = ConstEvalCtxt::new(cx).eval(r)?; if let Constant::Int(shift) = const_expr { return Some((dir, shift, l)); } diff --git a/clippy_lints/src/manual_slice_size_calculation.rs b/clippy_lints/src/manual_slice_size_calculation.rs index 429ee2637c2fa..b24a0f4695a6c 100644 --- a/clippy_lints/src/manual_slice_size_calculation.rs +++ b/clippy_lints/src/manual_slice_size_calculation.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::{expr_or_init, in_constant, std_or_core}; +use clippy_utils::{expr_or_init, is_in_const_context, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation { && BinOpKind::Mul == op.node && !expr.span.from_expansion() // Does not apply inside const because size_of_val is not cost in stable. - && !in_constant(cx, expr.hir_id) + && !is_in_const_context(cx) && let Some(receiver) = simplify(cx, left, right) { let ctxt = expr.span.ctxt(); diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 686ecccf8294c..85cabd2800a65 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,11 +1,12 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::usage::mutated_variables; use clippy_utils::{eq_expr_value, higher, match_def_path, paths}; use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind}; @@ -14,6 +15,7 @@ use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::Span; +use std::iter; declare_clippy_lint! { /// ### What it does @@ -108,19 +110,19 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { format!("stripping a {kind_word} manually"), |diag| { diag.span_note(test_span, format!("the {kind_word} was tested here")); - multispan_sugg( - diag, + diag.multipart_suggestion( format!("try using the `strip_{kind_word}` method"), - vec![( + iter::once(( test_span, format!( "if let Some(<stripped>) = {}.strip_{kind_word}({}) ", snippet(cx, target_arg.span, ".."), snippet(cx, pattern.span, "..") ), - )] - .into_iter() - .chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))), + )) + .chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))) + .collect(), + Applicability::HasPlaceholders, ); }, ); @@ -145,7 +147,7 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E // Returns the length of the `expr` if it's a constant string or char. fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> { - let value = constant(cx, cx.typeck_results(), expr)?; + let value = ConstEvalCtxt::new(cx).eval(expr)?; match value { Constant::Str(value) => Some(value.len() as u128), Constant::Char(value) => Some(value.len_utf8() as u128), @@ -183,9 +185,9 @@ fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> { } } -// Find expressions where `target` is stripped using the length of `pattern`. -// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}` -// method. +/// Find expressions where `target` is stripped using the length of `pattern`. +/// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}` +/// method. fn find_stripping<'tcx>( cx: &LateContext<'tcx>, strip_kind: StripKind, diff --git a/clippy_lints/src/manual_unwrap_or_default.rs b/clippy_lints/src/manual_unwrap_or_default.rs index f1acc4b217480..8f8390b6f3f9e 100644 --- a/clippy_lints/src/manual_unwrap_or_default.rs +++ b/clippy_lints/src/manual_unwrap_or_default.rs @@ -10,7 +10,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{in_constant, is_default_equivalent, peel_blocks, span_contains_comment}; +use clippy_utils::{is_default_equivalent, is_in_const_context, peel_blocks, span_contains_comment}; declare_clippy_lint! { /// ### What it does @@ -174,7 +174,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr) && !expr.span.from_expansion() - && !in_constant(cx, expr.hir_id) + && !is_in_const_context(cx) { handle(cx, if_let_or_match, expr); } diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 85a08f81c2f34..2d4c8daf5cb66 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::constant_simple; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; @@ -69,7 +69,7 @@ fn check_and_lint<'tcx>( && let Some(ty_name) = find_type_name(cx, ty) && let Some(or_body_snippet) = snippet_opt(cx, else_expr.span) && let Some(indent) = indent_of(cx, expr.span) - && constant_simple(cx, cx.typeck_results(), else_expr).is_some() + && ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some() { lint(cx, expr, let_expr, ty_name, or_body_snippet, indent); } diff --git a/clippy_lints/src/matches/match_ref_pats.rs b/clippy_lints/src/matches/match_ref_pats.rs index aba4c85c59e28..5445ee1f04296 100644 --- a/clippy_lints/src/matches/match_ref_pats.rs +++ b/clippy_lints/src/matches/match_ref_pats.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::sugg::Sugg; use core::iter::once; @@ -54,7 +54,11 @@ where span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| { if !expr.span.from_expansion() { - multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs)); + diag.multipart_suggestion( + msg, + first_sugg.chain(remaining_suggs).collect(), + Applicability::MachineApplicable, + ); } }); } diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 8d22ceb47f859..91e40e4275c07 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; @@ -148,23 +148,27 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { Applicability::MaybeIncorrect, ), variants => { - let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); - let message = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden { - suggestions.push("_".into()); - "wildcard matches known variants and will also match future added variants" + let (message, add_wildcard) = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden { + ( + "wildcard matches known variants and will also match future added variants", + true, + ) } else { - "wildcard match will also match any future added variants" + ("wildcard match will also match any future added variants", false) }; - span_lint_and_sugg( - cx, - WILDCARD_ENUM_MATCH_ARM, - wildcard_span, - message, - "try", - suggestions.join(" | "), - Applicability::MaybeIncorrect, - ); + span_lint_and_then(cx, WILDCARD_ENUM_MATCH_ARM, wildcard_span, message, |diag| { + let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); + if add_wildcard { + suggestions.push("_".into()); + } + diag.span_suggestion( + wildcard_span, + "try", + suggestions.join(" | "), + Applicability::MaybeIncorrect, + ); + }); }, }; } @@ -176,9 +180,8 @@ enum CommonPrefixSearcher<'a> { } impl<'a> CommonPrefixSearcher<'a> { fn with_path(&mut self, path: &'a [PathSegment<'a>]) { - match path { - [path @ .., _] => self.with_prefix(path), - [] => (), + if let [path @ .., _] = path { + self.with_prefix(path); } } diff --git a/clippy_lints/src/matches/match_wild_err_arm.rs b/clippy_lints/src/matches/match_wild_err_arm.rs index 310675d01a204..d0d2025878e48 100644 --- a/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/clippy_lints/src/matches/match_wild_err_arm.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; -use clippy_utils::{in_constant, is_wild, peel_blocks_with_stmt}; +use clippy_utils::{is_in_const_context, is_wild, peel_blocks_with_stmt}; use rustc_hir::{Arm, Expr, PatKind}; use rustc_lint::LateContext; use rustc_span::symbol::{kw, sym}; @@ -11,7 +11,7 @@ use super::MATCH_WILD_ERR_ARM; pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) { // `unwrap`/`expect` is not (yet) const, so we want to allow this in const contexts for now - if in_constant(cx, ex.hir_id) { + if is_in_const_context(cx) { return; } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index cec3504ed8d09..cf5377e0725dd 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -27,7 +27,7 @@ mod wild_in_or_pats; use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::source::walk_span_to_context; -use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, span_contains_cfg}; +use clippy_utils::{higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg}; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -1069,7 +1069,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_str_case_mismatch::check(cx, ex, arms); redundant_guards::check(cx, arms, &self.msrv); - if !in_constant(cx, expr.hir_id) { + if !is_in_const_context(cx) { manual_unwrap_or::check_match(cx, expr, ex, arms); manual_map::check_match(cx, expr, ex, arms); manual_filter::check_match(cx, ex, arms, expr); @@ -1098,7 +1098,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { else_expr, ); } - if !in_constant(cx, expr.hir_id) { + if !is_in_const_context(cx) { manual_unwrap_or::check_if_let( cx, expr, diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 45b375dbe3d72..71f211be925bf 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, constant_full_int, mir_to_const, FullInt}; +use clippy_utils::consts::{mir_to_const, ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; @@ -33,22 +33,20 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) .filter_map(|arm| { if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { - let lhs_const = match lhs { - Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, - None => { - let min_val_const = ty.numeric_min_val(cx.tcx)?; - mir_to_const(cx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? - }, + let lhs_const = if let Some(lhs) = lhs { + ConstEvalCtxt::new(cx).eval(lhs)? + } else { + let min_val_const = ty.numeric_min_val(cx.tcx)?; + mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? }; - let rhs_const = match rhs { - Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, - None => { - let max_val_const = ty.numeric_max_val(cx.tcx)?; - mir_to_const(cx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? - }, + let rhs_const = if let Some(rhs) = rhs { + ConstEvalCtxt::new(cx).eval(rhs)? + } else { + let max_val_const = ty.numeric_max_val(cx.tcx)?; + mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? }; - let lhs_val = lhs_const.int_value(cx, ty)?; - let rhs_val = rhs_const.int_value(cx, ty)?; + let lhs_val = lhs_const.int_value(cx.tcx, ty)?; + let rhs_val = rhs_const.int_value(cx.tcx, ty)?; let rhs_bound = match range_end { RangeEnd::Included => EndBound::Included(rhs_val), RangeEnd::Excluded => EndBound::Excluded(rhs_val), @@ -60,7 +58,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) } if let PatKind::Lit(value) = pat.kind { - let value = constant_full_int(cx, cx.typeck_results(), value)?; + let value = ConstEvalCtxt::new(cx).eval_full_int(value)?; return Some(SpannedRange { span: pat.span, node: (value, EndBound::Included(value)), diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index c2c0fbf439d51..8222c3057f73e 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::source::snippet; use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used}; -use clippy_utils::{in_constant, path_to_local}; +use clippy_utils::{is_in_const_context, path_to_local}; use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -116,7 +116,7 @@ fn check_method_calls<'tcx>( // `s if s.is_empty()` becomes "" // `arr if arr.is_empty()` becomes [] - if ty.is_str() && !in_constant(cx, if_expr.hir_id) { + if ty.is_str() && !is_in_const_context(cx) { r#""""#.into() } else if slice_like { "[]".into() diff --git a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index 316b2f63e4eaa..2154cd5b24a54 100644 --- a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::{Pat, PatKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -15,13 +15,15 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { && fields.len() == def.non_enum_variant().fields.len() && !def.non_enum_variant().is_field_list_non_exhaustive() { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, REST_PAT_IN_FULLY_BOUND_STRUCTS, pat.span, "unnecessary use of `..` pattern in struct binding. All fields were already bound", - None, - "consider removing `..` from this binding", + |diag| { + diag.help("consider removing `..` from this binding"); + }, ); } } diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 99fdbcff890b4..24dea03601c5f 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,12 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{expr_block, snippet, SpanRangeExt}; -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 clippy_utils::ty::implements_trait; +use clippy_utils::{ + is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, +}; +use core::ops::ControlFlow; +use rustc_arena::DroplessArena; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingMode, Block, Expr, ExprKind, Pat, PatKind}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::intravisit::{walk_pat, Visitor}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, AdtDef, ParamEnv, TyCtxt, TypeckResults, VariantDef}; use rustc_span::{sym, Span}; use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; @@ -27,52 +32,58 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { } #[rustfmt::skip] -pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { - if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() { - if expr.span.from_expansion() { - // Don't lint match expressions present in - // macro_rules! block - return; - } - if let PatKind::Or(..) = arms[0].pat.kind { - // don't lint for or patterns for now, this makes - // the lint noisy in unnecessary situations - return; - } - let els = arms[1].body; - let els = if is_unit_expr(peel_blocks(els)) && !empty_arm_has_comment(cx, els.span) { +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>) { + if let [arm1, arm2] = arms + && arm1.guard.is_none() + && arm2.guard.is_none() + && !expr.span.from_expansion() + // don't lint for or patterns for now, this makes + // the lint noisy in unnecessary situations + && !matches!(arm1.pat.kind, PatKind::Or(..)) + { + let els = if is_unit_expr(peel_blocks(arm2.body)) && !empty_arm_has_comment(cx, arm2.body.span) { None - } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind { - if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() { + } else if let ExprKind::Block(block, _) = arm2.body.kind { + if matches!((block.stmts, block.expr), ([], Some(_)) | ([_], None)) { // single statement/expr "else" block, don't lint return; } // block with 2+ statements or 1 expr and 1+ statement - Some(els) + Some(arm2.body) } else { // not a block or an empty block w/ comments, don't lint return; }; - let ty = cx.typeck_results().expr_ty(ex); - if (*ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id)) && - (check_single_pattern(arms) || check_opt_like(cx, arms, ty)) { - report_single_pattern(cx, ex, arms, expr, els); + let typeck = cx.typeck_results(); + if *typeck.expr_ty(ex).peel_refs().kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) { + let mut v = PatVisitor { + typeck, + has_enum: false, + }; + if v.visit_pat(arm2.pat).is_break() { + return; + } + if v.has_enum { + let cx = PatCtxt { + tcx: cx.tcx, + param_env: cx.param_env, + typeck, + arena: DroplessArena::default(), + }; + let mut state = PatState::Other; + if !(state.add_pat(&cx, arm2.pat) || state.add_pat(&cx, arm1.pat)) { + // Don't lint if the pattern contains an enum which doesn't have a wild match. + return; + } + } + + report_single_pattern(cx, ex, arm1, expr, els); } } } -fn check_single_pattern(arms: &[Arm<'_>]) -> bool { - is_wild(arms[1].pat) -} - -fn report_single_pattern( - cx: &LateContext<'_>, - ex: &Expr<'_>, - arms: &[Arm<'_>], - expr: &Expr<'_>, - els: Option<&Expr<'_>>, -) { +fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, expr: &Expr<'_>, els: Option<&Expr<'_>>) { let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH }; let ctxt = expr.span.ctxt(); let mut app = Applicability::MachineApplicable; @@ -80,9 +91,9 @@ fn report_single_pattern( format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app)) }); - let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat); + let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind - && let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex)) + && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() && (ty.is_integral() @@ -114,17 +125,17 @@ fn report_single_pattern( snippet(cx, ex.span, ".."), // PartialEq for different reference counts may not exist. "&".repeat(ref_count_diff), - snippet(cx, arms[0].pat.span, ".."), - expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app), + snippet(cx, arm.pat.span, ".."), + expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app), ); (msg, sugg) } else { let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"; let sugg = format!( "if let {} = {} {}{els_str}", - snippet(cx, arms[0].pat.span, ".."), + snippet(cx, arm.pat.span, ".."), snippet(cx, ex.span, ".."), - expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app), + expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app), ); (msg, sugg) }; @@ -132,106 +143,227 @@ fn report_single_pattern( span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app); } -fn check_opt_like<'a>(cx: &LateContext<'a>, arms: &[Arm<'_>], ty: Ty<'a>) -> bool { - // We don't want to lint if the second arm contains an enum which could - // have more variants in the future. - form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) +struct PatVisitor<'tcx> { + typeck: &'tcx TypeckResults<'tcx>, + has_enum: bool, } - -/// Returns `true` if all of the types in the pattern are enums which we know -/// won't be expanded in the future -fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool { - let mut paths_and_types = Vec::new(); - collect_pat_paths(&mut paths_and_types, cx, pat, ty); - paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty)) +impl<'tcx> Visitor<'tcx> for PatVisitor<'tcx> { + type Result = ControlFlow<()>; + fn visit_pat(&mut self, pat: &'tcx Pat<'_>) -> Self::Result { + if matches!(pat.kind, PatKind::Binding(..)) { + ControlFlow::Break(()) + } else { + self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().map_or(false, AdtDef::is_enum); + walk_pat(self, pat) + } + } } -/// Returns `true` if the given type is an enum we know won't be expanded in the future -fn in_candidate_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - // list of candidate `Enum`s we know will never get any more members - let candidates = [sym::Cow, sym::Option, sym::Result]; +/// The context needed to manipulate a `PatState`. +struct PatCtxt<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + arena: DroplessArena, +} - for candidate_ty in candidates { - if is_type_diagnostic_item(cx, ty, candidate_ty) { - return true; +/// State for tracking whether a match can become non-exhaustive by adding a variant to a contained +/// enum. +/// +/// This treats certain std enums as if they will never be extended. +enum PatState<'a> { + /// Either a wild match or an uninteresting type. Uninteresting types include: + /// * builtin types (e.g. `i32` or `!`) + /// * A struct/tuple/array containing only uninteresting types. + /// * A std enum containing only uninteresting types. + Wild, + /// A std enum we know won't be extended. Tracks the states of each variant separately. + /// + /// This is not used for `Option` since it uses the current pattern to track it's state. + StdEnum(&'a mut [PatState<'a>]), + /// Either the initial state for a pattern or a non-std enum. There is currently no need to + /// distinguish these cases. + /// + /// For non-std enums there's no need to track the state of sub-patterns as the state of just + /// this pattern on it's own is enough for linting. Consider two cases: + /// * This enum has no wild match. This case alone is enough to determine we can lint. + /// * This enum has a wild match and therefore all sub-patterns also have a wild match. + /// + /// In both cases the sub patterns are not needed to determine whether to lint. + Other, +} +impl<'a> PatState<'a> { + /// Adds a set of patterns as a product type to the current state. Returns whether or not the + /// current state is a wild match after the merge. + fn add_product_pat<'tcx>( + &mut self, + cx: &'a PatCtxt<'tcx>, + pats: impl IntoIterator<Item = &'tcx Pat<'tcx>>, + ) -> bool { + // Ideally this would actually keep track of the state separately for each pattern. Doing so would + // require implementing something similar to exhaustiveness checking which is a significant increase + // in complexity. + // + // For now treat this as a wild match only if all the sub-patterns are wild + let is_wild = pats.into_iter().all(|p| { + let mut state = Self::Other; + state.add_pat(cx, p) + }); + if is_wild { + *self = Self::Wild; } + is_wild } - false -} -/// Collects types from the given pattern -fn collect_pat_paths<'a>(acc: &mut Vec<Ty<'a>>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) { - match pat.kind { - PatKind::Tuple(inner, _) => inner.iter().for_each(|p| { - let p_ty = cx.typeck_results().pat_ty(p); - collect_pat_paths(acc, cx, p, p_ty); - }), - PatKind::TupleStruct(..) | PatKind::Binding(BindingMode::NONE, .., None) | PatKind::Path(_) => { - acc.push(ty); - }, - _ => {}, + /// Attempts to get the state for the enum variant, initializing the current state if necessary. + fn get_std_enum_variant<'tcx>( + &mut self, + cx: &'a PatCtxt<'tcx>, + adt: AdtDef<'tcx>, + path: &'tcx QPath<'_>, + hir_id: HirId, + ) -> Option<(&mut Self, &'tcx VariantDef)> { + let states = match self { + Self::Wild => return None, + Self::Other => { + *self = Self::StdEnum(cx.arena.alloc_from_iter((0..adt.variants().len()).map(|_| Self::Other))); + let Self::StdEnum(x) = self else { + unreachable!(); + }; + x + }, + Self::StdEnum(x) => x, + }; + let i = match cx.typeck.qpath_res(path, hir_id) { + Res::Def(DefKind::Ctor(..), id) => adt.variant_index_with_ctor_id(id), + Res::Def(DefKind::Variant, id) => adt.variant_index_with_id(id), + _ => return None, + }; + Some((&mut states[i.as_usize()], adt.variant(i))) } -} -/// Returns true if the given arm of pattern matching contains wildcard patterns. -fn contains_only_wilds(pat: &Pat<'_>) -> bool { - match pat.kind { - PatKind::Wild => true, - PatKind::Tuple(inner, _) | PatKind::TupleStruct(_, inner, ..) => inner.iter().all(contains_only_wilds), - _ => false, + fn check_all_wild_enum(&mut self) -> bool { + if let Self::StdEnum(states) = self + && states.iter().all(|s| matches!(s, Self::Wild)) + { + *self = Self::Wild; + true + } else { + false + } } -} -/// Returns true if the given patterns forms only exhaustive matches that don't contain enum -/// patterns without a wildcard. -fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, right: &Pat<'_>) -> bool { - match (&left.kind, &right.kind) { - (PatKind::Wild, _) | (_, PatKind::Wild) => true, - (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => { - // We don't actually know the position and the presence of the `..` (dotdot) operator - // in the arms, so we need to evaluate the correct offsets here in order to iterate in - // both arms at the same time. - let left_pos = left_pos.as_opt_usize(); - let right_pos = right_pos.as_opt_usize(); - let len = max( - left_in.len() + usize::from(left_pos.is_some()), - right_in.len() + usize::from(right_pos.is_some()), - ); - let mut left_pos = left_pos.unwrap_or(usize::MAX); - let mut right_pos = right_pos.unwrap_or(usize::MAX); - let mut left_dot_space = 0; - let mut right_dot_space = 0; - for i in 0..len { - let mut found_dotdot = false; - if i == left_pos { - left_dot_space += 1; - if left_dot_space < len - left_in.len() { - left_pos += 1; - } - found_dotdot = true; - } - if i == right_pos { - right_dot_space += 1; - if right_dot_space < len - right_in.len() { - right_pos += 1; - } - found_dotdot = true; - } - if found_dotdot { - continue; + #[expect(clippy::similar_names)] + fn add_struct_pats<'tcx>( + &mut self, + cx: &'a PatCtxt<'tcx>, + pat: &'tcx Pat<'tcx>, + path: &'tcx QPath<'tcx>, + single_pat: Option<&'tcx Pat<'tcx>>, + pats: impl IntoIterator<Item = &'tcx Pat<'tcx>>, + ) -> bool { + let ty::Adt(adt, _) = *cx.typeck.pat_ty(pat).kind() else { + // Should never happen + *self = Self::Wild; + return true; + }; + if adt.is_struct() { + return if let Some(pat) = single_pat + && adt.non_enum_variant().fields.len() == 1 + { + self.add_pat(cx, pat) + } else { + self.add_product_pat(cx, pats) + }; + } + match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => { + if let Some(pat) = single_pat { + self.add_pat(cx, pat) + } else { + *self = Self::Wild; + true } - if !contains_only_wilds(&left_in[i - left_dot_space]) - && !contains_only_wilds(&right_in[i - right_dot_space]) + }, + Some(sym::Result | sym::Cow) => { + let Some((state, variant)) = self.get_std_enum_variant(cx, adt, path, pat.hir_id) else { + return matches!(self, Self::Wild); + }; + let is_wild = if let Some(pat) = single_pat + && variant.fields.len() == 1 { - return false; - } - } - true - }, - (PatKind::TupleStruct(..), PatKind::Path(_)) => pat_in_candidate_enum(cx, ty, right), - (PatKind::TupleStruct(..), PatKind::TupleStruct(_, inner, _)) => { - pat_in_candidate_enum(cx, ty, right) && inner.iter().all(contains_only_wilds) - }, - _ => false, + state.add_pat(cx, pat) + } else { + state.add_product_pat(cx, pats) + }; + is_wild && self.check_all_wild_enum() + }, + _ => matches!(self, Self::Wild), + } + } + + /// Adds the pattern into the current state. Returns whether or not the current state is a wild + /// match after the merge. + #[expect(clippy::similar_names)] + fn add_pat<'tcx>(&mut self, cx: &'a PatCtxt<'tcx>, pat: &'tcx Pat<'_>) -> bool { + match pat.kind { + PatKind::Path(_) + if match *cx.typeck.pat_ty(pat).peel_refs().kind() { + ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()), + ty::Tuple(tys) => !tys.is_empty(), + ty::Array(_, len) => len.try_eval_target_usize(cx.tcx, cx.param_env) != Some(1), + ty::Slice(..) => true, + _ => false, + } => + { + matches!(self, Self::Wild) + }, + + // Patterns for things which can only contain a single sub-pattern. + PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => { + self.add_pat(cx, pat) + }, + PatKind::Tuple([sub_pat], pos) + if pos.as_opt_usize().is_none() || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 => + { + self.add_pat(cx, sub_pat) + }, + PatKind::Slice([sub_pat], _, []) | PatKind::Slice([], _, [sub_pat]) + if let ty::Array(_, len) = *cx.typeck.pat_ty(pat).kind() + && len.try_eval_target_usize(cx.tcx, cx.param_env) == Some(1) => + { + self.add_pat(cx, sub_pat) + }, + + PatKind::Or(pats) => pats.iter().any(|p| self.add_pat(cx, p)), + PatKind::Tuple(pats, _) => self.add_product_pat(cx, pats), + PatKind::Slice(head, _, tail) => self.add_product_pat(cx, head.iter().chain(tail)), + + PatKind::TupleStruct(ref path, pats, _) => self.add_struct_pats( + cx, + pat, + path, + if let [pat] = pats { Some(pat) } else { None }, + pats.iter(), + ), + PatKind::Struct(ref path, pats, _) => self.add_struct_pats( + cx, + pat, + path, + if let [pat] = pats { Some(pat.pat) } else { None }, + pats.iter().map(|p| p.pat), + ), + + PatKind::Wild + | PatKind::Binding(_, _, _, None) + | PatKind::Lit(_) + | PatKind::Range(..) + | PatKind::Path(_) + | PatKind::Never + | PatKind::Err(_) => { + *self = PatState::Wild; + true + }, + } } } diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index dd489fc250b7b..b7ffa8b8a7814 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; @@ -48,29 +48,28 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine return; }; - let expr_err_ty = cx.typeck_results().expr_ty(err_arg); - let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); - let mut applicability = Applicability::MachineApplicable; - let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); - let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { - "" // already returns - } else { - "return " - }; - let suggestion = if err_ty == expr_err_ty { - format!("{ret_prefix}{prefix}{origin_snippet}{suffix}") - } else { - format!("{ret_prefix}{prefix}{origin_snippet}.into(){suffix}") - }; - - span_lint_and_sugg( + span_lint_and_then( cx, TRY_ERR, expr.span, "returning an `Err(_)` with the `?` operator", - "try", - suggestion, - applicability, + |diag| { + let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); + let mut applicability = Applicability::MachineApplicable; + let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); + let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { + "" // already returns + } else { + "return " + }; + let suggestion = if err_ty == expr_err_ty { + format!("{ret_prefix}{prefix}{origin_snippet}{suffix}") + } else { + format!("{ret_prefix}{prefix}{origin_snippet}.into(){suffix}") + }; + diag.span_suggestion(expr.span, "try", suggestion, applicability); + }, ); } } diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 20f3722e173ad..2a8a5fcc3b6f9 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,5 +1,5 @@ use super::{contains_return, BIND_INSTEAD_OF_MAP}; -use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::peel_blocks; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::visitors::find_all_ret_expressions; @@ -136,15 +136,16 @@ impl BindInsteadOfMap { return false; }; span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, msg, |diag| { - multispan_sugg_with_applicability( - diag, - "try", + diag.multipart_suggestion( + format!("use `{}` instead", self.good_method_name), + std::iter::once((span, self.good_method_name.into())) + .chain( + suggs + .into_iter() + .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), + ) + .collect(), Applicability::MachineApplicable, - std::iter::once((span, self.good_method_name.into())).chain( - suggs - .into_iter() - .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), - ), ); }); true diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 926bd06bacbd2..e0826b53004b6 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir as hir; @@ -29,19 +29,22 @@ pub(super) fn check( sym::RcWeak | sym::ArcWeak => "Weak", _ => return, }; - - // Sometimes unnecessary ::<_> after Rc/Arc/Weak - let mut app = Applicability::Unspecified; - let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0; - - span_lint_and_sugg( + span_lint_and_then( cx, CLONE_ON_REF_PTR, expr.span, "using `.clone()` on a ref-counted pointer", - "try", - format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), - app, + |diag| { + // Sometimes unnecessary ::<_> after Rc/Arc/Weak + let mut app = Applicability::Unspecified; + let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0; + diag.span_suggestion( + expr.span, + "try", + format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), + app, + ); + }, ); } } diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index eab536b88a5b3..2ab0401947c4c 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; @@ -33,6 +33,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr span = expr.span; } let lint_msg = format!("`{lint_unary}FileType::is_file()` only {verb} regular files"); - let help_msg = format!("use `{help_unary}FileType::is_dir()` instead"); - span_lint_and_help(cx, FILETYPE_IS_FILE, span, lint_msg, None, help_msg); + + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, FILETYPE_IS_FILE, span, lint_msg, |diag| { + diag.help(format!("use `{help_unary}FileType::is_dir()` instead")); + }); } diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index 455274a442854..c6285c87a2636 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -1,5 +1,5 @@ use super::utils::derefs_to_slice; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; @@ -19,9 +19,7 @@ pub(super) fn check<'tcx>( ) { // Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`, // because they do not implement `IndexMut` - let mut applicability = Applicability::MachineApplicable; let expr_ty = cx.typeck_results().expr_ty(recv); - let get_args_str = snippet_with_applicability(cx, get_arg.span, "..", &mut applicability); let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() { "slice" } else if is_type_diagnostic_item(cx, expr_ty, sym::Vec) { @@ -58,24 +56,34 @@ pub(super) fn check<'tcx>( }; let mut_str = if is_mut { "_mut" } else { "" }; - let borrow_str = if !needs_ref { - "" - } else if is_mut { - "&mut " - } else { - "&" - }; - span_lint_and_sugg( + span_lint_and_then( cx, GET_UNWRAP, span, - format!("called `.get{mut_str}().unwrap()` on a {caller_type}. Using `[]` is more clear and more concise"), - "try", - format!( - "{borrow_str}{}[{get_args_str}]", - snippet_with_applicability(cx, recv.span, "..", &mut applicability) - ), - applicability, + format!("called `.get{mut_str}().unwrap()` on a {caller_type}"), + |diag| { + let mut applicability = Applicability::MachineApplicable; + let get_args_str = snippet_with_applicability(cx, get_arg.span, "..", &mut applicability); + + let borrow_str = if !needs_ref { + "" + } else if is_mut { + "&mut " + } else { + "&" + }; + + diag.span_suggestion_with_style( + span, + "using `[]` is clearer and more concise", + format!( + "{borrow_str}{}[{get_args_str}]", + snippet_with_applicability(cx, recv.span, "..", &mut applicability) + ), + applicability, + rustc_errors::SuggestionStyle::ShowAlways, + ); + }, ); } diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index c510cd915d0c9..519091406ccf3 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{implements_trait, peel_mid_ty_refs}; -use clippy_utils::{is_diag_item_method, is_diag_trait_item}; +use clippy_utils::ty::implements_trait; +use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv && is_clone_like(cx, method_name, method_def_id) && let return_type = cx.typeck_results().expr_ty(expr) && let input_type = cx.typeck_results().expr_ty(recv) - && let (input_type, ref_count) = peel_mid_ty_refs(input_type) + && let (input_type, ref_count) = peel_middle_ty_refs(input_type) && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)) && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())) && return_type == input_type diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index 210e4ae0a7bc0..22d896433f07a 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -2,7 +2,7 @@ use super::IS_DIGIT_ASCII_RADIX; use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::consts::{constant_full_int, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( return; } - if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) { + if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix) { let (num, replacement) = match radix_val { FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"), FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"), diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs index d921b7ea14f5d..cc82f6cfd6384 100644 --- a/clippy_lints/src/methods/is_empty.rs +++ b/clippy_lints/src/methods/is_empty.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::constant_is_empty; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint; use clippy_utils::{find_binding_init, path_to_local}; use rustc_hir::{Expr, HirId}; @@ -18,7 +18,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ if !receiver.span.eq_ctxt(init_expr.span) { return; } - if let Some(init_is_empty) = constant_is_empty(cx, init_expr) { + if let Some(init_is_empty) = ConstEvalCtxt::new(cx).eval_is_empty(init_expr) { span_lint( cx, CONST_IS_EMPTY, diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 05e77386128f9..33de3b87abc82 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -1,15 +1,12 @@ -#![allow(unused_imports)] - use super::ITER_KV_MAP; use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::pat_is_wild; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{pat_is_wild, sugg}; -use rustc_hir::{BindingMode, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind}; -use rustc_lint::{LateContext, LintContext}; -use rustc_middle::ty; -use rustc_span::{sym, Span}; +use rustc_hir::{Body, Expr, ExprKind, PatKind}; +use rustc_lint::LateContext; +use rustc_span::sym; /// lint use of: /// diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 262a57ab591a6..9ff6eaa348710 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lang_item_or_ctor, is_trait_method}; @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir().get_parent_item(expr.hir_id)) && let def_id = item.owner_id.to_def_id() && is_trait_method(cx, expr, sym::Iterator) - && let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg) + && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext) { let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/iter_skip_zero.rs b/clippy_lints/src/methods/iter_skip_zero.rs index 6b696b42a6931..39e440e784f6d 100644 --- a/clippy_lints/src/methods/iter_skip_zero.rs +++ b/clippy_lints/src/methods/iter_skip_zero.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{is_from_proc_macro, is_trait_method}; use rustc_errors::Applicability; @@ -11,7 +11,7 @@ use super::ITER_SKIP_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) { if !expr.span.from_expansion() && is_trait_method(cx, expr, sym::Iterator) - && let Some(arg) = constant(cx, cx.typeck_results(), arg_expr).and_then(|constant| { + && let Some(arg) = ConstEvalCtxt::new(cx).eval(arg_expr).and_then(|constant| { if let Constant::Int(arg) = constant { Some(arg) } else { diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs index b631cd00cda43..9b358235a40df 100644 --- a/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_trait_method; use rustc_hir as hir; @@ -9,7 +9,7 @@ use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { if is_trait_method(cx, expr, sym::Iterator) { - if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg) { + if let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) { span_lint( cx, ITERATOR_STEP_BY_ZERO, diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index a5ba5e5d8918c..08ce7e204dd27 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -2,10 +2,10 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, should_call_clone_as_function}; -use clippy_utils::{is_diag_trait_item, match_def_path, paths, peel_blocks}; +use clippy_utils::{is_diag_trait_item, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty; @@ -114,7 +114,7 @@ fn handle_path( recv: &hir::Expr<'_>, ) { if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() - && match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD) + && cx.tcx.lang_items().get(LangItem::CloneFn) == Some(path_def_id) { // The `copied` and `cloned` methods are only available on `&T` and `&mut T` in `Option` // and `Result`. diff --git a/clippy_lints/src/methods/map_err_ignore.rs b/clippy_lints/src/methods/map_err_ignore.rs index fbb83c8ce5635..162f0ac564d7d 100644 --- a/clippy_lints/src/methods/map_err_ignore.rs +++ b/clippy_lints/src/methods/map_err_ignore.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -22,13 +22,17 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) { { // span the area of the closure capture and warn that the // original error will be thrown away - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, MAP_ERR_IGNORE, fn_decl_span, "`map_err(|_|...` wildcard pattern discards the original error", - None, - "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", + |diag| { + diag.help( + "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", + ); + }, ); } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index aa4cad9342a17..1d7b10fe8f045 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -133,7 +133,7 @@ mod zst_offset; use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; @@ -749,7 +749,6 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # #![allow(unused)] /// (0_i32..10) /// .filter(|n| n.checked_add(1).is_some()) /// .map(|n| n.checked_add(1).unwrap()); @@ -757,7 +756,6 @@ declare_clippy_lint! { /// /// Use instead: /// ```no_run - /// # #[allow(unused)] /// (0_i32..10).filter_map(|n| n.checked_add(1)); /// ``` #[clippy::version = "1.51.0"] @@ -850,7 +848,6 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # #![allow(unused)] /// let vec = vec![1]; /// vec.iter().find(|x| **x == 0).is_some(); /// @@ -862,7 +859,6 @@ declare_clippy_lint! { /// let vec = vec![1]; /// vec.iter().any(|x| *x == 0); /// - /// # #[allow(unused)] /// !"hello world".contains("world"); /// ``` #[clippy::version = "pre 1.29.0"] @@ -1505,7 +1501,6 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # #[allow(unused)] /// (0..3).fold(false, |acc, x| acc || x > 2); /// ``` /// @@ -1897,7 +1892,9 @@ declare_clippy_lint! { /// trait. /// /// ### Why is this bad? - /// It is recommended style to use collect. See + /// If it's needed to create a collection from the contents of an iterator, the `Iterator::collect(_)` + /// method is preferred. However, when it's needed to specify the container type, + /// `Vec::from_iter(_)` can be more readable than using a turbofish (e.g. `_.collect::<Vec<_>>()`). See /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) /// /// ### Example @@ -1916,6 +1913,14 @@ declare_clippy_lint! { /// /// assert_eq!(v, vec![5, 5, 5, 5, 5]); /// ``` + /// but prefer to use + /// ```no_run + /// let numbers: Vec<i32> = FromIterator::from_iter(1..=5); + /// ``` + /// instead of + /// ```no_run + /// let numbers = (1..=5).collect::<Vec<_>>(); + /// ``` #[clippy::version = "1.49.0"] pub FROM_ITER_INSTEAD_OF_COLLECT, pedantic, @@ -2008,13 +2013,11 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # #[allow(unused)] /// "Hello".bytes().nth(3); /// ``` /// /// Use instead: /// ```no_run - /// # #[allow(unused)] /// "Hello".as_bytes().get(3); /// ``` #[clippy::version = "1.52.0"] @@ -2059,7 +2062,6 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # #![allow(unused)] /// let some_vec = vec![0, 1, 2, 3]; /// /// some_vec.iter().count(); @@ -3656,7 +3658,6 @@ declare_clippy_lint! { /// /// ### Example /// ```no_run - /// # #![allow(unused)] /// let owned_string = "This is a string".to_owned(); /// owned_string.as_str().as_bytes() /// # ; @@ -3664,7 +3665,6 @@ declare_clippy_lint! { /// /// Use instead: /// ```no_run - /// # #![allow(unused)] /// let owned_string = "This is a string".to_owned(); /// owned_string.as_bytes() /// # ; @@ -4915,13 +4915,13 @@ impl Methods { str_split::check(cx, expr, recv, arg); }, ("splitn" | "rsplitn", [count_arg, pat_arg]) => { - if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) { + if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { - if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) { + if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); } }, diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs index d425b505a760c..cbeb48b6cc376 100644 --- a/clippy_lints/src/methods/open_options.rs +++ b/clippy_lints/src/methods/open_options.rs @@ -151,7 +151,7 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument, S cx, NONSENSICAL_OPEN_OPTIONS, prev_span, - format!("the method `{}` is called more than once", &option), + format!("the method `{option}` is called more than once"), ); } } diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index bb4cdd2a6fa10..7837517ed5d8a 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_lang_item; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, repeat_arg: &'tcx Expr<'_>, ) { - if constant(cx, cx.typeck_results(), repeat_arg) == Some(Constant::Int(1)) { + if ConstEvalCtxt::new(cx).eval(repeat_arg) == Some(Constant::Int(1)) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); if ty.is_str() { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 4f42fb73547a7..12cabd43cb1b2 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,5 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; @@ -301,7 +301,7 @@ fn parse_iter_usage<'tcx>( }; }, ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if let Some(Constant::Int(idx)) = constant(cx, cx.typeck_results(), idx_expr) { + if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval(idx_expr) { let span = if name.ident.as_str() == "nth" { e.span } else if let Some((_, Node::Expr(next_expr))) = iter.next() diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index 78851d4122f12..86c0a6322b666 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -3,7 +3,7 @@ use std::cmp::Ordering; use super::UNNECESSARY_MIN_OR_MAX; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::consts::{constant, constant_with_source, Constant, ConstantSource, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt}; use clippy_utils::source::snippet; use rustc_errors::Applicability; @@ -20,10 +20,9 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, ) { let typeck_results = cx.typeck_results(); - if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = - constant_with_source(cx, typeck_results, recv) - && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = - constant_with_source(cx, typeck_results, arg) + let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck_results); + if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) + && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) { let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { return; @@ -78,9 +77,9 @@ enum Extrema { fn detect_extrema<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Extrema> { let ty = cx.typeck_results().expr_ty(expr); - let cv = constant(cx, cx.typeck_results(), expr)?; + let cv = ConstEvalCtxt::new(cx).eval(expr)?; - match (cv.int_value(cx, ty)?, ty.kind()) { + match (cv.int_value(cx.tcx, ty)?, ty.kind()) { (FullInt::S(i), &ty::Int(ity)) if i == i128::MIN >> (128 - ity.bit_width()?) => Some(Extrema::Minimum), (FullInt::S(i), &ty::Int(ity)) if i == i128::MAX >> (128 - ity.bit_width()?) => Some(Extrema::Maximum), (FullInt::U(i), &ty::Uint(uty)) if i == u128::MAX >> (128 - uty.bit_width()?) => Some(Extrema::Maximum), diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 5d899415d7728..fed2b128dcf30 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,12 +3,11 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::{ - get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_mid_ty_refs, -}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, match_def_path, paths, return_ty, + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, match_def_path, paths, peel_middle_ty_refs, + return_ty, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -120,8 +119,8 @@ fn check_addr_of_expr( }, ] = adjustments[..] && let receiver_ty = cx.typeck_results().expr_ty(receiver) - && let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty) - && let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty) + && let (target_ty, n_target_refs) = peel_middle_ty_refs(*target_ty) + && let (receiver_ty, n_receiver_refs) = peel_middle_ty_refs(receiver_ty) // Only flag cases satisfying at least one of the following three conditions: // * the referent and receiver types are distinct // * the referent/receiver type is a copyable array @@ -382,7 +381,7 @@ fn check_other_call_arg<'tcx>( && let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder() && let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id) && let Some(input) = fn_sig.inputs().get(i) - && let (input, n_refs) = peel_mid_ty_refs(*input) + && let (input, n_refs) = peel_middle_ty_refs(*input) && let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input) && let Some(sized_def_id) = cx.tcx.lang_items().sized_trait() && let [trait_predicate] = trait_predicates diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index 8b8a965b9f0c9..3004d9c4233a7 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_hir_and_then}; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; use rustc_errors::Applicability; @@ -97,10 +97,8 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, enumerate_span, "you seem to use `.enumerate()` and immediately discard the index", |diag| { - multispan_sugg_with_applicability( - diag, + diag.multipart_suggestion( "remove the `.enumerate()` call", - Applicability::MachineApplicable, vec![ (closure_param.span, new_closure_param), ( @@ -108,6 +106,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, String::new(), ), ], + Applicability::MachineApplicable, ); }, ); diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index ae2b6e6347eb0..ebad4ae6ee95d 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -2,10 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{should_call_clone_as_function, walk_ptrs_ty_depth}; use clippy_utils::{ - get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, paths, peel_blocks, strip_pat_refs, + get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs, }; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; @@ -104,7 +104,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, fn check_qpath(cx: &LateContext<'_>, qpath: hir::QPath<'_>, hir_id: hir::HirId) -> bool { // We check it's calling the `clone` method of the `Clone` trait. if let Some(path_def_id) = cx.qpath_res(&qpath, hir_id).opt_def_id() { - match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD) + cx.tcx.lang_items().get(LangItem::CloneFn) == Some(path_def_id) } else { false } diff --git a/clippy_lints/src/methods/verbose_file_reads.rs b/clippy_lints/src/methods/verbose_file_reads.rs index 181b413a18288..8ed61637eca21 100644 --- a/clippy_lints/src/methods/verbose_file_reads.rs +++ b/clippy_lints/src/methods/verbose_file_reads.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_method; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::{Expr, ExprKind, QPath}; @@ -23,6 +23,9 @@ pub(super) fn check<'tcx>( && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _))) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File) { - span_lint_and_help(cx, VERBOSE_FILE_READS, expr.span, msg, None, help); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, VERBOSE_FILE_READS, expr.span, msg, |diag| { + diag.help(help); + }); } } diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 28068c6347325..7384e534ed7d7 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -127,7 +127,7 @@ pub(super) fn check<'tcx>( .collect::<Vec<_>>() .join(" and "); - format!("methods with the following characteristics: ({})", &s) + format!("methods with the following characteristics: ({s})") } else { format!("methods called {}", &conventions[0]) } diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index c3fbca1d560e6..e95864c6db8b5 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_trait_method; use rustc_hir::{Expr, ExprKind}; @@ -106,15 +106,11 @@ fn fetch_const<'a, 'tcx>( if args.next().is_some() { return None; } - constant_simple(cx, cx.typeck_results(), first_arg).map_or_else( - || constant_simple(cx, cx.typeck_results(), second_arg).map(|c| (m, c, first_arg)), - |c| { - if constant_simple(cx, cx.typeck_results(), second_arg).is_none() { - // otherwise ignore - Some((m, c, second_arg)) - } else { - None - } - }, - ) + let ecx = ConstEvalCtxt::new(cx); + match (ecx.eval_simple(first_arg), ecx.eval_simple(second_arg)) { + (Some(c), None) => Some((m, c, second_arg)), + (None, Some(c)) => Some((m, c, first_arg)), + // otherwise ignore + _ => None, + } } diff --git a/clippy_lints/src/misc_early/literal_suffix.rs b/clippy_lints/src/misc_early/literal_suffix.rs index e0a5e401a50aa..22467999cd8d5 100644 --- a/clippy_lints/src/misc_early/literal_suffix.rs +++ b/clippy_lints/src/misc_early/literal_suffix.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_lint::EarlyContext; use rustc_span::Span; @@ -12,24 +12,36 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str, suffi // Do not lint when literal is unsuffixed. if !suffix.is_empty() { if lit_snip.as_bytes()[maybe_last_sep_idx] == b'_' { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, SEPARATED_LITERAL_SUFFIX, 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]), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + lit_span, + "remove the underscore", + format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]), + Applicability::MachineApplicable, + ); + }, ); } else { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, UNSEPARATED_LITERAL_SUFFIX, lit_span, format!("{sugg_type} type suffix should be separated by an underscore"), - "add an underscore", - format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + lit_span, + "add an underscore", + format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]), + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/clippy_lints/src/misc_early/unneeded_field_pattern.rs index cb305cf5582d1..6db03adf44ac9 100644 --- a/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Pat, PatKind}; use rustc_lint::EarlyContext; @@ -21,13 +21,15 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { } } if !pfields.is_empty() && wilds == pfields.len() { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, UNNEEDED_FIELD_PATTERN, pat.span, "all the struct fields are matched to a wildcard pattern, consider using `..`", - None, - format!("try with `{type_name} {{ .. }}` instead"), + |diag| { + diag.help(format!("try with `{type_name} {{ .. }}` instead")); + }, ); return; } @@ -56,14 +58,15 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { } } - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, UNNEEDED_FIELD_PATTERN, field.span, - "you matched a field with a wildcard pattern, consider using `..` \ - instead", - None, - format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", ")), + "you matched a field with a wildcard pattern, consider using `..` instead", + |diag| { + diag.help(format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", "))); + }, ); } } diff --git a/clippy_lints/src/missing_assert_message.rs b/clippy_lints/src/missing_assert_message.rs index 935ed48dacc50..a9ea11f4c2b06 100644 --- a/clippy_lints/src/missing_assert_message.rs +++ b/clippy_lints/src/missing_assert_message.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_test; use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn}; use rustc_hir::Expr; @@ -79,13 +79,15 @@ impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage { }; if let PanicExpn::Empty = panic_expn { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, MISSING_ASSERT_MESSAGE, macro_call.span, "assert without any message", - None, - "consider describing why the failing assert is problematic", + |diag| { + diag.help("consider describing why the failing assert is problematic"); + }, ); } } diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 68c158330ab53..052d738b2e79e 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -186,7 +186,7 @@ fn already_const(header: hir::FnHeader) -> bool { fn could_be_const_with_abi(cx: &LateContext<'_>, msrv: &Msrv, abi: Abi) -> bool { match abi { Abi::Rust => true, - // `const extern "C"` was stablized after 1.62.0 + // `const extern "C"` was stabilized after 1.62.0 Abi::C { unwind: false } => msrv.meets(msrvs::CONST_EXTERN_FN), // Rest ABIs are still unstable and need the `const_extern_fn` feature enabled. _ => cx.tcx.features().const_extern_fn, diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs index 85029a5e6a0d4..7ee746365d102 100644 --- a/clippy_lints/src/missing_trait_methods.rs +++ b/clippy_lints/src/missing_trait_methods.rs @@ -1,10 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use clippy_utils::macros::span_is_local; -use rustc_hir::def_id::DefIdMap; +use rustc_hir::def_id::DefIdSet; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::AssocItem; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -68,33 +67,26 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { }) = 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)) + let trait_item_ids: DefIdSet = items + .iter() + .filter_map(|impl_item| impl_item.trait_item_def_id) .collect(); - for impl_item in *items { - if let Some(def_id) = impl_item.trait_item_def_id { - provided.remove(&def_id); - } + for assoc in cx + .tcx + .provided_trait_methods(trait_id) + .filter(|assoc| !trait_item_ids.contains(&assoc.def_id)) + { + span_lint_and_then( + cx, + MISSING_TRAIT_METHODS, + cx.tcx.def_span(item.owner_id), + format!("missing trait method provided by default: `{}`", assoc.name), + |diag| { + diag.span_help(cx.tcx.def_span(assoc.def_id), "implement the method"); + }, + ); } - - cx.tcx.with_stable_hashing_context(|hcx| { - for assoc in provided.values_sorted(&hcx, true) { - 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 9c5a8a0cfcdfa..6964d8c8dbb33 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; @@ -324,13 +324,17 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { if path_to_local_id(expr, self.var) { // Check that this is a read, not a write. if !is_in_assignment_position(self.cx, expr) { - span_lint_and_note( + span_lint_and_then( self.cx, MIXED_READ_WRITE_IN_EXPRESSION, expr.span, format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)), - Some(self.write_expr.span), - "whether read occurs before this write depends on evaluation order", + |diag| { + diag.span_note( + self.write_expr.span, + "whether read occurs before this write depends on evaluation order", + ); + }, ); } } diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index 305499f9da43c..e9c5f64a2550d 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; @@ -121,17 +121,18 @@ impl EarlyLintPass for ModStyle { for folder in &folder_segments { if !mod_folders.contains(folder) { if let Some((file, path)) = file_map.get(folder) { - let mut correct = path.to_path_buf(); - correct.pop(); - correct.push(folder); - correct.push("mod.rs"); - span_lint_and_help( + span_lint_and_then( cx, SELF_NAMED_MODULE_FILES, Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), format!("`mod.rs` files are required, found `{}`", path.display()), - None, - format!("move `{}` to `{}`", path.display(), correct.display(),), + |diag| { + let mut correct = path.to_path_buf(); + correct.pop(); + correct.push(folder); + correct.push("mod.rs"); + diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),)); + }, ); } } @@ -161,17 +162,18 @@ fn process_paths_for_mod_files<'a>( /// for code-sharing between tests. fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { if path.ends_with("mod.rs") && !path.starts_with("tests") { - let mut mod_file = path.to_path_buf(); - mod_file.pop(); - mod_file.set_extension("rs"); - - span_lint_and_help( + span_lint_and_then( cx, MOD_MODULE_FILES, Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), format!("`mod.rs` files are not allowed, found `{}`", path.display()), - None, - format!("move `{}` to `{}`", path.display(), mod_file.display()), + |diag| { + let mut mod_file = path.to_path_buf(); + mod_file.pop(); + mod_file.set_extension("rs"); + + diag.help(format!("move `{}` to `{}`", path.display(), mod_file.display())); + }, ); } } diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index 67f9b52c352e6..32e7fde03b2c8 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -1,7 +1,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::mir::PossibleBorrowerMap; +use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode}; @@ -12,6 +12,7 @@ use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; 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::{ self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty, }; @@ -107,6 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> { } && let count = needless_borrow_count( cx, + &mut self.possible_borrowers, fn_id, cx.typeck_results().node_args(hir_id), i, @@ -155,9 +157,14 @@ fn path_has_args(p: &QPath<'_>) -> bool { /// The following constraints will be checked: /// * The borrowed expression meets all the generic type's constraints. /// * The generic type appears only once in the functions signature. -/// * The borrowed value is Copy itself OR not a variable (created by a function call) +/// * The borrowed value is: +/// - `Copy` itself, or +/// - the only use of a mutable reference, or +/// - not a variable (created by a function call) +#[expect(clippy::too_many_arguments)] fn needless_borrow_count<'tcx>( cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, fn_id: DefId, callee_args: ty::GenericArgsRef<'tcx>, arg_index: usize, @@ -232,9 +239,9 @@ fn needless_borrow_count<'tcx>( let referent_ty = cx.typeck_results().expr_ty(referent); - if (!is_copy(cx, referent_ty) && !referent_ty.is_ref()) - && let ExprKind::AddrOf(_, _, inner) = reference.kind - && !matches!(inner.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) + if !(is_copy(cx, referent_ty) + || referent_ty.is_ref() && referent_used_exactly_once(cx, possible_borrowers, reference) + || matches!(referent.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))) { return false; } @@ -337,6 +344,37 @@ fn is_mixed_projection_predicate<'tcx>( } } +fn referent_used_exactly_once<'tcx>( + cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + reference: &Expr<'tcx>, +) -> bool { + if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id) + && let Some(local) = expr_local(cx.tcx, reference) + && let [location] = *local_assignments(mir, local).as_slice() + && let block_data = &mir.basic_blocks[location.block] + && let Some(statement) = block_data.statements.get(location.statement_index) + && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind + && !place.is_indirect_first_projection() + { + 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 `args`, 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`. diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index f2e00cef7e9ff..a0bbf6b14b217 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_self; use clippy_utils::ptr::get_spans; use clippy_utils::source::{snippet, snippet_opt}; @@ -278,9 +278,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } } - let spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))]; - - multispan_sugg(diag, "consider taking a reference instead", spans); + diag.span_suggestion( + input.span, + "consider taking a reference instead", + format!("&{}", snippet(cx, input.span, "_")), + Applicability::MaybeIncorrect, + ); }; span_lint_and_then( diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 9d326c06eff65..8232e69db3917 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -273,7 +273,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } let snippet = if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) { - format!("assert!({}.len() > {});", &arr, &func) + format!("assert!({arr}.len() > {func});") } else { return; }; diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 15bb328b4465a..6915cd4061551 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -6,7 +6,7 @@ use std::ptr; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::in_constant; +use clippy_utils::is_in_const_context; use clippy_utils::macros::macro_backtrace; use clippy_utils::ty::{implements_trait, InteriorMut}; use rustc_hir::def::{DefKind, Res}; @@ -406,7 +406,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Path(qpath) = &expr.kind { // Only lint if we use the const item inside a function. - if in_constant(cx, expr.hir_id) { + if is_in_const_context(cx) { return; } diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 8b8aabe7accc6..aadd729f32a45 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -243,7 +243,6 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { owner_id, .. }) => { - #[allow(trivial_casts)] if let Node::Item(item) = cx.tcx.parent_hir_node(owner_id.into()) && let Some(trait_ref) = cx .tcx diff --git a/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/clippy_lints/src/operators/absurd_extreme_comparisons.rs index 9769da6d3e9b1..a0de5ea711ca0 100644 --- a/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -3,7 +3,7 @@ use rustc_lint::LateContext; use rustc_middle::ty; use clippy_utils::comparisons::{normalize_comparison, Rel}; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use clippy_utils::ty::is_isize_or_usize; @@ -121,7 +121,7 @@ fn detect_absurd_comparison<'tcx>( fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> { let ty = cx.typeck_results().expr_ty(expr); - let cv = constant(cx, cx.typeck_results(), expr)?; + let cv = ConstEvalCtxt::new(cx).eval(expr)?; let which = match (ty.kind(), cv) { (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum, diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index a7e381be743d5..bc71a4790b9d4 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,6 +1,6 @@ use super::ARITHMETIC_SIDE_EFFECTS; use clippy_config::Conf; -use clippy_utils::consts::{constant, constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary}; @@ -162,7 +162,7 @@ impl ArithmeticSideEffects { { return Some(n.get()); } - if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), expr) { + if let Some(Constant::Int(n)) = ConstEvalCtxt::new(cx).eval(expr) { return Some(n); } None @@ -200,7 +200,7 @@ impl ArithmeticSideEffects { lhs: &'tcx hir::Expr<'_>, rhs: &'tcx hir::Expr<'_>, ) { - if constant_simple(cx, cx.typeck_results(), expr).is_some() { + if ConstEvalCtxt::new(cx).eval_simple(expr).is_some() { return; } if !matches!( @@ -280,7 +280,7 @@ impl ArithmeticSideEffects { let Some(arg) = args.first() else { return; }; - if constant_simple(cx, cx.typeck_results(), receiver).is_some() { + if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() { return; } let instance_ty = cx.typeck_results().expr_ty(receiver); @@ -308,7 +308,7 @@ impl ArithmeticSideEffects { let hir::UnOp::Neg = un_op else { return; }; - if constant(cx, cx.typeck_results(), un_expr).is_some() { + if ConstEvalCtxt::new(cx).eval(un_expr).is_some() { return; } let ty = cx.typeck_results().expr_ty(expr).peel_refs(); diff --git a/clippy_lints/src/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs index 545e680ce0de5..4414056a467ca 100644 --- a/clippy_lints/src/operators/bit_mask.rs +++ b/clippy_lints/src/operators/bit_mask.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -166,7 +166,7 @@ fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: } fn fetch_int_literal(cx: &LateContext<'_>, lit: &Expr<'_>) -> Option<u128> { - match constant(cx, cx.typeck_results(), lit)? { + match ConstEvalCtxt::new(cx).eval(lit)? { Constant::Int(n) => Some(n), _ => None, } diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs index 7bf9b8ef866a2..c131752439628 100644 --- a/clippy_lints/src/operators/const_comparisons.rs +++ b/clippy_lints/src/operators/const_comparisons.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::layout::HasTyCtxt; @@ -20,13 +20,14 @@ use super::{IMPOSSIBLE_COMPARISONS, REDUNDANT_COMPARISONS}; // Flip yoda conditionals, turnings expressions like `42 < x` into `x > 42` fn comparison_to_const<'tcx>( cx: &LateContext<'tcx>, - typeck: &TypeckResults<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, expr: &'tcx Expr<'tcx>, ) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> { if let ExprKind::Binary(operator, left, right) = expr.kind && let Ok(cmp_op) = CmpOp::try_from(operator.node) { - match (constant(cx, typeck, left), constant(cx, typeck, right)) { + let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck); + match (ecx.eval(left), ecx.eval(right)) { (Some(_), Some(_)) => None, (_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))), (Some(con), _) => Some((cmp_op.reverse(), right, left, con, typeck.expr_ty(left))), diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index ca3112ce5c468..e3029f8438e5f 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; @@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>( if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) - && let Some(Constant::Int(divisor)) = constant(cx, cx.typeck_results(), right) + && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval(right) { let suggested_fn = match (method_path.ident.as_str(), divisor) { ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis", diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs index 066e08f3bd4ca..24bfe2b050bb5 100644 --- a/clippy_lints/src/operators/erasing_op.rs +++ b/clippy_lints/src/operators/erasing_op.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::same_type_and_consts; @@ -34,12 +34,12 @@ fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) fn check_op<'tcx>( cx: &LateContext<'tcx>, - tck: &TypeckResults<'tcx>, + tck: &'tcx TypeckResults<'tcx>, op: &Expr<'tcx>, other: &Expr<'tcx>, parent: &Expr<'tcx>, ) { - if constant_simple(cx, tck, op) == Some(Constant::Int(0)) { + if ConstEvalCtxt::with_env(cx.tcx, cx.param_env, tck).eval_simple(op) == Some(Constant::Int(0)) { if different_types(tck, other, parent) { return; } diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index 0e5b440c50f27..df6e67455965e 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_with_source, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_item_name; use clippy_utils::sugg::Sugg; @@ -17,12 +17,14 @@ pub(crate) fn check<'tcx>( right: &'tcx Expr<'_>, ) { if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { - let left_is_local = match constant_with_source(cx, cx.typeck_results(), left) { + let typeck = cx.typeck_results(); + let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck); + let left_is_local = match ecx.eval_with_source(left) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, }; - let right_is_local = match constant_with_source(cx, cx.typeck_results(), right) { + let right_is_local = match ecx.eval_with_source(right) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index 0879dcd9bcdf6..830be50c8ba4f 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{clip, peel_hir_expr_refs, unsext}; @@ -184,14 +184,13 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Exp && cx.typeck_results().expr_ty(right).peel_refs().is_integral() // `1 << 0` is a common pattern in bit manipulation code && !(cmp == BinOpKind::Shl - && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0)) - && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))) + && ConstEvalCtxt::new(cx).eval_simple(right) == Some(Constant::Int(0)) + && ConstEvalCtxt::new(cx).eval_simple(left) == Some(Constant::Int(1))) } fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { - let lhs_const = constant_full_int(cx, cx.typeck_results(), left); - let rhs_const = constant_full_int(cx, cx.typeck_results(), right); - if match (lhs_const, rhs_const) { + let ecx = ConstEvalCtxt::new(cx); + if match (ecx.eval_full_int(left), ecx.eval_full_int(right)) { (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(), (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, @@ -201,7 +200,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span } fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) -> bool { - if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) { + if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), ty::Uint(uty) => clip(cx.tcx, !0, uty), diff --git a/clippy_lints/src/operators/integer_division.rs b/clippy_lints/src/operators/integer_division.rs index 631d10f4a72e9..76eba7327cff6 100644 --- a/clippy_lints/src/operators/integer_division.rs +++ b/clippy_lints/src/operators/integer_division.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir as hir; use rustc_lint::LateContext; @@ -15,13 +15,9 @@ pub(crate) fn check<'tcx>( && cx.typeck_results().expr_ty(left).is_integral() && cx.typeck_results().expr_ty(right).is_integral() { - span_lint_and_help( - cx, - INTEGER_DIVISION, - expr.span, - "integer division", - None, - "division of integers may cause loss of precision. consider using floats", - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| { + diag.help("division of integers may cause loss of precision. consider using floats"); + }); } } diff --git a/clippy_lints/src/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs index d65fffac5a828..c83bdda347a66 100644 --- a/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/clippy_lints/src/operators/modulo_arithmetic.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sext; use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; @@ -42,15 +42,11 @@ fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }; if op.node == BinOpKind::Eq || op.node == BinOpKind::Ne { - if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), rhs) { - return true; - } - if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), lhs) { - return true; - } + let ecx = ConstEvalCtxt::new(cx); + matches!(ecx.eval(lhs), Some(Constant::Int(0))) || matches!(ecx.eval(rhs), Some(Constant::Int(0))) + } else { + false } - - false } struct OperandInfo { @@ -60,7 +56,7 @@ struct OperandInfo { } fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OperandInfo> { - match constant(cx, cx.typeck_results(), operand) { + match ConstEvalCtxt::new(cx).eval(operand) { Some(Constant::Int(v)) => match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => { let value = sext(cx.tcx, v, ity); diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index ea933168cfd5d..565294bb40a82 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -1,5 +1,5 @@ use super::FLOAT_ARITHMETIC; -use clippy_utils::consts::constant_simple; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; use rustc_lint::LateContext; @@ -55,7 +55,7 @@ impl Context { return; } let ty = cx.typeck_results().expr_ty(arg); - if constant_simple(cx, cx.typeck_results(), expr).is_none() && ty.is_floating_point() { + if ConstEvalCtxt::new(cx).eval_simple(expr).is_none() && ty.is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_id = Some(expr.hir_id); } diff --git a/clippy_lints/src/operators/op_ref.rs b/clippy_lints/src/operators/op_ref.rs index c2b27c9b2298a..82b9d10fbeb1d 100644 --- a/clippy_lints/src/operators/op_ref.rs +++ b/clippy_lints/src/operators/op_ref.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_enclosing_block; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; @@ -64,10 +64,10 @@ pub(crate) fn check<'tcx>( |diag| { let lsnip = snippet(cx, l.span, "...").to_string(); let rsnip = snippet(cx, r.span, "...").to_string(); - multispan_sugg( - diag, + diag.multipart_suggestion( "use the values directly", vec![(left.span, lsnip), (right.span, rsnip)], + Applicability::MachineApplicable, ); }, ); diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index d4906328ccb93..ae02d22512e31 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::{ - can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_res_lang_ctor, peel_blocks, - peel_hir_expr_while, CaptureKind, + can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, + peel_blocks, peel_hir_expr_while, CaptureKind, }; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -294,7 +294,7 @@ fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { // Don't lint macros and constants - if expr.span.from_expansion() || in_constant(cx, expr.hir_id) { + if expr.span.from_expansion() || is_in_const_context(cx) { return; } diff --git a/clippy_lints/src/partial_pub_fields.rs b/clippy_lints/src/partial_pub_fields.rs index 2d20cbea698f2..267e2067e101a 100644 --- a/clippy_lints/src/partial_pub_fields.rs +++ b/clippy_lints/src/partial_pub_fields.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Item, ItemKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -57,24 +57,16 @@ impl EarlyLintPass for PartialPubFields { 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", - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, PARTIAL_PUB_FIELDS, field.vis.span, msg, |diag| { + diag.help("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", - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, PARTIAL_PUB_FIELDS, field.vis.span, msg, |diag| { + diag.help("consider using public field here"); + }); return; } } diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index 0008f154ae3a8..18bfb588a1187 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -21,8 +21,8 @@ declare_clippy_lint! { /// `PathBuf::from` instead. /// /// ### Known problems - /// `.join()` introduces an implicit `clone()`. `PathBuf::from` can alternativly be - /// used when the `PathBuf` is newly constructed. This will avoild the implicit clone. + /// `.join()` introduces an implicit `clone()`. `PathBuf::from` can alternatively be + /// used when the `PathBuf` is newly constructed. This will avoid the implicit clone. /// /// ### Example /// ```rust diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 9661a57b8b95c..c1296b04387a0 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::{ intravisit, Body, Expr, ExprKind, FnDecl, LetExpr, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind, }; @@ -133,23 +133,25 @@ enum DerefPossible { fn apply_lint(cx: &LateContext<'_>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool { let maybe_mismatch = find_first_mismatch(cx, pat); if let Some((span, mutability, level)) = maybe_mismatch { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, PATTERN_TYPE_MISMATCH, span, "type of pattern does not match the expression type", - None, - format!( - "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings", - match (deref_possible, level) { - (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ", - _ => "", - }, - match mutability { - Mutability::Mut => "&mut _", - Mutability::Not => "&_", - }, - ), + |diag| { + diag.help(format!( + "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings", + match (deref_possible, level) { + (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ", + _ => "", + }, + match mutability { + Mutability::Mut => "&mut _", + Mutability::Not => "&_", + }, + )); + }, ); true } else { diff --git a/clippy_lints/src/pub_use.rs b/clippy_lints/src/pub_use.rs index ab8f8a1689dc3..5b973a79eae36 100644 --- a/clippy_lints/src/pub_use.rs +++ b/clippy_lints/src/pub_use.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -42,14 +42,10 @@ impl EarlyLintPass for PubUse { if let ItemKind::Use(_) = item.kind && let VisibilityKind::Public = item.vis.kind { - span_lint_and_help( - cx, - PUB_USE, - item.span, - "using `pub use`", - None, - "move the exported item to a public module instead", - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, PUB_USE, item.span, "using `pub use`", |diag| { + diag.help("move the exported item to a public module instead"); + }); } } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index ef6b4d3aeab27..e1e3ded2c795d 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -7,7 +7,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ - eq_expr_value, higher, in_constant, is_else_clause, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, + eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, span_contains_comment, }; @@ -346,15 +346,13 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { return; } - if !self.inside_try_block() && !in_constant(cx, stmt.hir_id) { + if !self.inside_try_block() && !is_in_const_context(cx) { check_let_some_else_return_none(cx, stmt); } self.check_manual_let_else(cx, stmt); } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !self.inside_try_block() - && !in_constant(cx, expr.hir_id) - && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) + if !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) { check_is_none_or_err_and_early_return(cx, expr); check_if_let_some_or_err_and_early_return(cx, expr); diff --git a/clippy_lints/src/question_mark_used.rs b/clippy_lints/src/question_mark_used.rs index f5e6cb804da17..0a974bf9d2f71 100644 --- a/clippy_lints/src/question_mark_used.rs +++ b/clippy_lints/src/question_mark_used.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use rustc_hir::{Expr, ExprKind, MatchSource}; @@ -39,13 +39,15 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed { return; } - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, QUESTION_MARK_USED, expr.span, "question mark operator was used", - None, - "consider using a custom macro or match expression", + |diag| { + diag.help("consider using a custom macro or match expression"); + }, ); } } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 829fb58bc652b..81189fe517c00 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,10 +1,10 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, higher, in_constant, is_integer_const, path_to_local}; +use clippy_utils::{get_parent_expr, higher, is_in_const_context, is_integer_const, path_to_local}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, HirId}; @@ -202,7 +202,7 @@ fn check_possible_range_contains( expr: &Expr<'_>, span: Span, ) { - if in_constant(cx, expr.hir_id) { + if is_in_const_context(cx) { return; } @@ -319,7 +319,7 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> _ => return None, }; if let Some(id) = path_to_local(l) { - if let Some(c) = constant(cx, cx.typeck_results(), r) { + if let Some(c) = ConstEvalCtxt::new(cx).eval(r) { return Some(RangeBounds { val: c, expr: r, @@ -331,7 +331,7 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> }); } } else if let Some(id) = path_to_local(r) { - if let Some(c) = constant(cx, cx.typeck_results(), l) { + if let Some(c) = ConstEvalCtxt::new(cx).eval(l) { return Some(RangeBounds { val: c, expr: l, @@ -451,8 +451,9 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { }) = higher::Range::hir(expr) && let ty = cx.typeck_results().expr_ty(start) && let ty::Int(_) | ty::Uint(_) = ty.kind() - && let Some(start_idx) = constant(cx, cx.typeck_results(), start) - && let Some(end_idx) = constant(cx, cx.typeck_results(), end) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(start_idx) = ecx.eval(start) + && let Some(end_idx) = ecx.eval(end) && let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx) && is_empty_range(limits, ordering) { diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 3416a93e3c4ac..a1231c082e68a 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let (fn_def_id, arg, arg_ty, clone_ret) = unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind)); - let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD) + let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id) || cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id) || (cx.tcx.is_diagnostic_item(sym::to_string_method, fn_def_id) && is_type_lang_item(cx, arg_ty, LangItem::String)); diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 82f22ad693d78..1c10e84d3caf6 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,7 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{is_type_lang_item, peel_mid_ty_refs}; +use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{get_parent_expr, peel_middle_ty_refs}; use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -82,13 +82,12 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { && let ExprKind::Index(indexed, range, _) = addressee.kind && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull) { - let (expr_ty, expr_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(expr)); - let (indexed_ty, indexed_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(indexed)); + let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr)); + let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed)); let parent_expr = get_parent_expr(cx, expr); let needs_parens_for_prefix = parent_expr.map_or(false, |parent| parent.precedence().order() > PREC_PREFIX); - let mut app = Applicability::MachineApplicable; - let ((lint, msg), help, sugg) = if expr_ty == indexed_ty { + if expr_ty == indexed_ty { if expr_ref_count > indexed_ref_count { // Indexing takes self by reference and can't return a reference to that // reference as it's a local variable. The only way this could happen is if @@ -99,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { } let deref_count = indexed_ref_count - expr_ref_count; - let (lint, reborrow_str, help_str) = if mutability == Mutability::Mut { + let ((lint, msg), reborrow_str, help_msg) = if mutability == Mutability::Mut { // The slice was used to reborrow the mutable reference. (DEREF_BY_SLICING_LINT, "&mut *", "reborrow the original value instead") } else if matches!( @@ -113,8 +112,11 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { a.kind, Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })) ) - }) { - // The slice was used to make a temporary reference. + }) || (matches!( + cx.typeck_results().expr_ty(indexed).ref_mutability(), + Some(Mutability::Mut) + ) && mutability == Mutability::Not) + { (DEREF_BY_SLICING_LINT, "&*", "reborrow the original value instead") } else if deref_count != 0 { (DEREF_BY_SLICING_LINT, "", "dereference the original value instead") @@ -122,38 +124,36 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { (REDUNDANT_SLICING_LINT, "", "use the original value instead") }; - let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; - let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix { - format!("({reborrow_str}{}{snip})", "*".repeat(deref_count)) - } else { - format!("{reborrow_str}{}{snip}", "*".repeat(deref_count)) - }; - - (lint, help_str, sugg) + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; + let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix { + format!("({reborrow_str}{}{snip})", "*".repeat(deref_count)) + } else { + format!("{reborrow_str}{}{snip}", "*".repeat(deref_count)) + }; + diag.span_suggestion(expr.span, help_msg, sugg, app); + }); } else if let Some(target_id) = cx.tcx.lang_items().deref_target() { if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( cx.param_env, Ty::new_projection_from_args(cx.tcx, target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])), ) { if deref_ty == expr_ty { - let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; - let sugg = if needs_parens_for_prefix { - format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) - } else { - format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) - }; - (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg) - } else { - return; + let (lint, msg) = DEREF_BY_SLICING_LINT; + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; + let sugg = if needs_parens_for_prefix { + format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) + } else { + format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) + }; + diag.span_suggestion(expr.span, "dereference the original value instead", sugg, app); + }); } - } else { - return; } - } else { - return; - }; - - span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg, app); + } } } } diff --git a/clippy_lints/src/ref_patterns.rs b/clippy_lints/src/ref_patterns.rs index 467038523b496..002c6c41d52ba 100644 --- a/clippy_lints/src/ref_patterns.rs +++ b/clippy_lints/src/ref_patterns.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{BindingMode, Pat, PatKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -33,14 +33,10 @@ impl EarlyLintPass for RefPatterns { if let PatKind::Ident(BindingMode::REF, _, _) = pat.kind && !pat.span.from_expansion() { - span_lint_and_help( - cx, - REF_PATTERNS, - pat.span, - "usage of ref pattern", - None, - "consider using `&` for clarity instead", - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, REF_PATTERNS, pat.span, "usage of ref pattern", |diag| { + diag.help("consider using `&` for clarity instead"); + }); } } } diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 8cacb646f5148..95014b230431c 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::snippet_opt; use clippy_utils::{def_path_def_ids, path_def_id, paths}; @@ -148,7 +148,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescape } fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<String> { - constant(cx, cx.typeck_results(), e).and_then(|c| match c { + ConstEvalCtxt::new(cx).eval(e).and_then(|c| match c { Constant::Str(s) => Some(s), _ => None, }) diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs deleted file mode 100644 index 8e999f3e89af3..0000000000000 --- a/clippy_lints/src/renamed_lints.rs +++ /dev/null @@ -1,65 +0,0 @@ -// This file is managed by `cargo dev rename_lint`. Prefer using that when possible. - -#[rustfmt::skip] -pub static RENAMED_LINTS: &[(&str, &str)] = &[ - ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), - ("clippy::blacklisted_name", "clippy::disallowed_names"), - ("clippy::block_in_if_condition_expr", "clippy::blocks_in_conditions"), - ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_conditions"), - ("clippy::blocks_in_if_conditions", "clippy::blocks_in_conditions"), - ("clippy::box_vec", "clippy::box_collection"), - ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), - ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), - ("clippy::derive_hash_xor_eq", "clippy::derived_hash_with_manual_eq"), - ("clippy::disallowed_method", "clippy::disallowed_methods"), - ("clippy::disallowed_type", "clippy::disallowed_types"), - ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), - ("clippy::identity_conversion", "clippy::useless_conversion"), - ("clippy::if_let_some_result", "clippy::match_result_ok"), - ("clippy::incorrect_clone_impl_on_copy_type", "clippy::non_canonical_clone_impl"), - ("clippy::incorrect_partial_ord_impl_on_ord_type", "clippy::non_canonical_partial_ord_impl"), - ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), - ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), - ("clippy::new_without_default_derive", "clippy::new_without_default"), - ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), - ("clippy::option_expect_used", "clippy::expect_used"), - ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"), - ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"), - ("clippy::option_unwrap_used", "clippy::unwrap_used"), - ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"), - ("clippy::ref_in_deref", "clippy::needless_borrow"), - ("clippy::result_expect_used", "clippy::expect_used"), - ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"), - ("clippy::result_unwrap_used", "clippy::unwrap_used"), - ("clippy::single_char_push_str", "clippy::single_char_add_str"), - ("clippy::stutter", "clippy::module_name_repetitions"), - ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"), - ("clippy::to_string_in_display", "clippy::recursive_format_impl"), - ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), - ("clippy::zero_width_space", "clippy::invisible_characters"), - ("clippy::cast_ref_to_mut", "invalid_reference_casting"), - ("clippy::clone_double_ref", "suspicious_double_ref_op"), - ("clippy::cmp_nan", "invalid_nan_comparisons"), - ("clippy::drop_bounds", "drop_bounds"), - ("clippy::drop_copy", "dropping_copy_types"), - ("clippy::drop_ref", "dropping_references"), - ("clippy::fn_null_check", "useless_ptr_null_checks"), - ("clippy::for_loop_over_option", "for_loops_over_fallibles"), - ("clippy::for_loop_over_result", "for_loops_over_fallibles"), - ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), - ("clippy::forget_copy", "forgetting_copy_types"), - ("clippy::forget_ref", "forgetting_references"), - ("clippy::into_iter_on_array", "array_into_iter"), - ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), - ("clippy::invalid_ref", "invalid_value"), - ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), - ("clippy::let_underscore_drop", "let_underscore_drop"), - ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), - ("clippy::panic_params", "non_fmt_panics"), - ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), - ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), - ("clippy::undropped_manually_drops", "undropped_manually_drops"), - ("clippy::unknown_clippy_lints", "unknown_lints"), - ("clippy::unused_label", "unused_labels"), - ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"), -]; diff --git a/clippy_lints/src/repeat_vec_with_capacity.rs b/clippy_lints/src/repeat_vec_with_capacity.rs index 792d8fc88f0bb..678681ea4252d 100644 --- a/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/clippy_lints/src/repeat_vec_with_capacity.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::macros::matching_root_macro_call; @@ -69,7 +69,7 @@ fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) { && let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr) && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY)) && !len_expr.span.from_expansion() - && let Some(Constant::Int(2..)) = constant(cx, cx.typeck_results(), expr_or_init(cx, len_expr)) + && let Some(Constant::Int(2..)) = ConstEvalCtxt::new(cx).eval(expr_or_init(cx, len_expr)) { emit_lint( cx, diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 8ced47b48a43d..13016cdadb072 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -395,7 +395,7 @@ fn check_final_expr<'tcx>( // Returns may be used to turn an expression into a statement in rustc's AST. // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) // `#[expect(clippy::needless_return)]` needs to be handled separatly to - // actually fullfil the expectation (clippy::#12998) + // actually fulfill the expectation (clippy::#12998) match cx.tcx.hir().attrs(expr.hir_id) { [] => {}, [attr] => { diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 7615c21276d69..09f1c11235291 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -92,11 +92,10 @@ impl SemicolonBlock { semi_span, "consider moving the `;` inside the block for consistent formatting", |diag| { - multispan_sugg_with_applicability( - diag, + diag.multipart_suggestion( "put the `;` here", + vec![(remove_span, String::new()), (insert_span, ";".to_owned())], Applicability::MachineApplicable, - [(remove_span, String::new()), (insert_span, ";".to_owned())], ); }, ); @@ -124,11 +123,10 @@ impl SemicolonBlock { block.span, "consider moving the `;` outside the block for consistent formatting", |diag| { - multispan_sugg_with_applicability( - diag, + diag.multipart_suggestion( "put the `;` here", + vec![(remove_span, String::new()), (insert_span, ";".to_owned())], Applicability::MachineApplicable, - [(remove_span, String::new()), (insert_span, ";".to_owned())], ); }, ); diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs index 5e65b9fa5171d..e6fe76493974a 100644 --- a/clippy_lints/src/set_contains_or_insert.rs +++ b/clippy_lints/src/set_contains_or_insert.rs @@ -12,8 +12,8 @@ use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does - /// Checks for usage of `contains` to see if a value is not - /// present on `HashSet` followed by a `insert`. + /// Checks for usage of `contains` to see if a value is not present + /// in a set like `HashSet` or `BTreeSet`, followed by an `insert`. /// /// ### Why is this bad? /// Using just `insert` and checking the returned `bool` is more efficient. @@ -45,12 +45,12 @@ declare_clippy_lint! { #[clippy::version = "1.80.0"] pub SET_CONTAINS_OR_INSERT, nursery, - "call to `HashSet::contains` followed by `HashSet::insert`" + "call to `<set>::contains` followed by `<set>::insert`" } -declare_lint_pass!(HashsetInsertAfterContains => [SET_CONTAINS_OR_INSERT]); +declare_lint_pass!(SetContainsOrInsert => [SET_CONTAINS_OR_INSERT]); -impl<'tcx> LateLintPass<'tcx> for HashsetInsertAfterContains { +impl<'tcx> LateLintPass<'tcx> for SetContainsOrInsert { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && let Some(higher::If { @@ -58,14 +58,14 @@ impl<'tcx> LateLintPass<'tcx> for HashsetInsertAfterContains { then: then_expr, .. }) = higher::If::hir(expr) - && let Some(contains_expr) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr) + && let Some((contains_expr, sym)) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr) && let Some(insert_expr) = find_insert_calls(cx, &contains_expr, then_expr) { span_lint( cx, SET_CONTAINS_OR_INSERT, vec![contains_expr.span, insert_expr.span], - "usage of `HashSet::insert` after `HashSet::contains`", + format!("usage of `{sym}::insert` after `{sym}::contains`"), ); } } @@ -77,7 +77,11 @@ struct OpExpr<'tcx> { span: Span, } -fn try_parse_op_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, symbol: Symbol) -> Option<OpExpr<'tcx>> { +fn try_parse_op_call<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + symbol: Symbol, +) -> Option<(OpExpr<'tcx>, Symbol)> { let expr = peel_hir_expr_while(expr, |e| { if let ExprKind::Unary(UnOp::Not, e) = e.kind { Some(e) @@ -97,11 +101,12 @@ fn try_parse_op_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, symbol: }); let receiver = receiver.peel_borrows(); let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); - if value.span.eq_ctxt(expr.span) - && is_type_diagnostic_item(cx, receiver_ty, sym::HashSet) - && path.ident.name == symbol - { - return Some(OpExpr { receiver, value, span }); + if value.span.eq_ctxt(expr.span) && path.ident.name == symbol { + for sym in &[sym::HashSet, sym::BTreeSet] { + if is_type_diagnostic_item(cx, receiver_ty, *sym) { + return Some((OpExpr { receiver, value, span }, *sym)); + } + } } } None @@ -113,7 +118,7 @@ fn find_insert_calls<'tcx>( expr: &'tcx Expr<'_>, ) -> Option<OpExpr<'tcx>> { for_each_expr(cx, expr, |e| { - if let Some(insert_expr) = try_parse_op_call(cx, e, sym!(insert)) + if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym!(insert)) && SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver) && SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value) { diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 80f5fd0b49441..7ae0310b6d996 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use rustc_data_structures::fx::FxHashMap; @@ -194,14 +194,9 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) (SHADOW_UNRELATED, msg) }, }; - span_lint_and_note( - cx, - lint, - span, - msg, - Some(cx.tcx.hir().span(shadowed)), - "previous binding is here", - ); + span_lint_and_then(cx, lint, span, msg, |diag| { + diag.span_note(cx.tcx.hir().span(shadowed), "previous binding is here"); + }); } /// Returns true if the expression is a simple transformation of a local binding such as `&x` diff --git a/clippy_lints/src/single_char_lifetime_names.rs b/clippy_lints/src/single_char_lifetime_names.rs index 72feb977c3104..d92b890950a3f 100644 --- a/clippy_lints/src/single_char_lifetime_names.rs +++ b/clippy_lints/src/single_char_lifetime_names.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{GenericParam, GenericParamKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -48,13 +48,15 @@ impl EarlyLintPass for SingleCharLifetimeNames { if let GenericParamKind::Lifetime = param.kind { if !param.is_placeholder && param.ident.as_str().len() <= 2 { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( ctx, SINGLE_CHAR_LIFETIME_NAMES, param.ident.span, "single-character lifetime names are likely uninformative", - None, - "use a more informative name", + |diag| { + diag.help("use a more informative name"); + }, ); } } diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index 8d7f12af86e8a..b3d32a6d7d84c 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_def_id; -use clippy_utils::ty::peel_mid_ty_refs; +use clippy_utils::{path_def_id, peel_middle_ty_refs}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -60,7 +59,7 @@ impl LateLintPass<'_> for SizeOfRef { && let Some(def_id) = path_def_id(cx, path) && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) && let arg_ty = cx.typeck_results().expr_ty(arg) - && peel_mid_ty_refs(arg_ty).1 > 1 + && peel_middle_ty_refs(arg_ty).1 > 1 { span_lint_and_help( cx, diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 12b70075a3d5e..974e21df817a8 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -1,11 +1,15 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_config::msrvs::Msrv; +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; +use rustc_attr::{StabilityLevel, StableSince}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, Path, PathSegment}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_semver::RustcVersion; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; use rustc_span::{sym, Span}; @@ -66,6 +70,10 @@ declare_clippy_lint! { /// imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint /// is also useful for crates migrating to become `no_std` compatible. /// + /// ### Known problems + /// The lint is only partially aware of the required MSRV for items that were originally in `std` but moved + /// to `core`. + /// /// ### Example /// ```no_run /// # extern crate alloc; @@ -81,20 +89,30 @@ declare_clippy_lint! { "type is imported from alloc when available in core" } -#[derive(Default)] pub struct StdReexports { // Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen // twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro // when the path could be also be used to access the module. prev_span: Span, + msrv: Msrv, } + +impl StdReexports { + pub fn new(conf: &'static Conf) -> Self { + Self { + prev_span: Span::default(), + msrv: conf.msrv.clone(), + } + } +} + impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]); impl<'tcx> LateLintPass<'tcx> for StdReexports { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { if let Res::Def(_, def_id) = path.res && let Some(first_segment) = get_first_segment(path) - && is_stable(cx, def_id) + && is_stable(cx, def_id, &self.msrv) && !in_external_macro(cx.sess(), path.span) && !is_from_proc_macro(cx, &first_segment.ident) { @@ -118,19 +136,27 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { _ => return, }; if first_segment.ident.span != self.prev_span { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, lint, first_segment.ident.span, format!("used import from `{used_mod}` instead of `{replace_with}`"), - format!("consider importing the item from `{replace_with}`"), - replace_with.to_string(), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + first_segment.ident.span, + format!("consider importing the item from `{replace_with}`"), + replace_with.to_string(), + Applicability::MachineApplicable, + ); + }, ); self.prev_span = first_segment.ident.span; } } } + + extract_msrv_attr!(LateContext); } /// Returns the first named segment of a [`Path`]. @@ -146,16 +172,29 @@ fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> } } -/// Checks if all ancestors of `def_id` are stable, to avoid linting -/// [unstable moves](https://github.com/rust-lang/rust/pull/95956) -fn is_stable(cx: &LateContext<'_>, mut def_id: DefId) -> bool { +/// Checks if all ancestors of `def_id` meet `msrv` to avoid linting [unstable moves](https://github.com/rust-lang/rust/pull/95956) +/// or now stable moves that were once unstable. +/// +/// Does not catch individually moved items +fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: &Msrv) -> bool { loop { - if cx - .tcx - .lookup_stability(def_id) - .map_or(false, |stability| stability.is_unstable()) + if let Some(stability) = cx.tcx.lookup_stability(def_id) + && let StabilityLevel::Stable { + since, + allowed_through_unstable_modules: false, + } = stability.level { - return false; + let stable = match since { + StableSince::Version(v) => { + msrv.meets(RustcVersion::new(v.major.into(), v.minor.into(), v.patch.into())) + }, + StableSince::Current => msrv.current().is_none(), + StableSince::Err => false, + }; + + if !stable { + return false; + } } match cx.tcx.opt_parent(def_id) { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 7da661485abfc..cfc387886dc01 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{ @@ -399,17 +399,16 @@ impl<'tcx> LateLintPass<'tcx> for StrToString { && let ty::Ref(_, ty, ..) = ty.kind() && ty.is_str() { - let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, self_arg.span, "..", &mut applicability); - - span_lint_and_sugg( + span_lint_and_then( cx, STR_TO_STRING, expr.span, "`to_string()` called on a `&str`", - "try", - format!("{snippet}.to_owned()"), - applicability, + |diag| { + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, self_arg.span, "..", &mut applicability); + diag.span_suggestion(expr.span, "try", format!("{snippet}.to_owned()"), applicability); + }, ); } } @@ -455,13 +454,15 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { && let ty = cx.typeck_results().expr_ty(self_arg) && is_type_lang_item(cx, ty, LangItem::String) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, STRING_TO_STRING, expr.span, "`to_string()` called on a `String`", - None, - "consider using `.clone()`", + |diag| { + diag.help("consider using `.clone()`"); + }, ); } } diff --git a/clippy_lints/src/suspicious_xor_used_as_pow.rs b/clippy_lints/src/suspicious_xor_used_as_pow.rs index d150a5f858aa3..d1d822a553260 100644 --- a/clippy_lints/src/suspicious_xor_used_as_pow.rs +++ b/clippy_lints/src/suspicious_xor_used_as_pow.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::source::snippet; use rustc_ast::LitKind; @@ -43,14 +43,19 @@ impl LateLintPass<'_> for ConfusingXorAndPow { && NumericLiteral::from_lit_kind(&snippet(cx, lit_right.span, ".."), &lit_right.node) .is_some_and(|x| x.is_decimal()) { - span_lint_and_sugg( + span_lint_and_then( cx, SUSPICIOUS_XOR_USED_AS_POW, expr.span, "`^` is not the exponentiation operator", - "did you mean to write", - format!("{}.pow({})", lit_left.node, lit_right.node), - Applicability::MaybeIncorrect, + |diag| { + diag.span_suggestion_verbose( + expr.span, + "did you mean to write", + format!("{}.pow({})", lit_left.node, lit_right.node), + Applicability::MaybeIncorrect, + ); + }, ); } } diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 93bad86580975..197011cde3a1a 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_indent, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}; +use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core}; use itertools::Itertools; use rustc_hir::intravisit::{walk_expr, Visitor}; @@ -170,7 +170,7 @@ fn generate_swap_warning<'tcx>( /// Implementation of the `MANUAL_SWAP` lint. fn check_manual_swap<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if in_constant(cx, block.hir_id) { + if is_in_const_context(cx) { return; } diff --git a/clippy_lints/src/tests_outside_test_module.rs b/clippy_lints/src/tests_outside_test_module.rs index 58e42892c41de..25d0a16e2ab45 100644 --- a/clippy_lints/src/tests_outside_test_module.rs +++ b/clippy_lints/src/tests_outside_test_module.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl}; @@ -61,13 +61,15 @@ impl LateLintPass<'_> for TestsOutsideTestModule { && is_in_test_function(cx.tcx, body.id().hir_id) && !is_in_cfg_test(cx.tcx, body.id().hir_id) { - span_lint_and_note( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, TESTS_OUTSIDE_TEST_MODULE, sp, "this function marked with #[test] is outside a #[cfg(test)] module", - None, - "move it to a testing module marked with #[cfg(test)]", + |diag| { + diag.note("move it to a testing module marked with #[cfg(test)]"); + }, ); } } diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 9c6813a54b943..373bf61d8ff94 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -21,7 +21,7 @@ mod wrong_transmute; use clippy_config::msrvs::Msrv; use clippy_config::Conf; -use clippy_utils::in_constant; +use clippy_utils::is_in_const_context; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -595,7 +595,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { // - from/to bits (https://github.com/rust-lang/rust/issues/73736) // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911) // - char conversions (https://github.com/rust-lang/rust/issues/89259) - let const_context = in_constant(cx, e.hir_id); + let const_context = is_in_const_context(cx); let (from_ty, from_ty_adjusted) = match cx.typeck_results().expr_adjustments(arg) { [] => (cx.typeck_results().expr_ty(arg), false), diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index b26365e34ab94..7acf3be51fb7e 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use rustc_hir::{Expr, ExprKind}; @@ -33,10 +33,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching: // transmute over constants that resolve to `null`. ExprKind::Path(ref _qpath) - if matches!( - constant(cx, cx.typeck_results(), casts_peeled), - Some(Constant::RawPtr(0)) - ) => + if matches!(ConstEvalCtxt::new(cx).eval(casts_peeled), Some(Constant::RawPtr(0))) => { lint_expr(cx, expr); true diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index 471bd44b5d5ee..544014bd32b30 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use rustc_hir::{Expr, ExprKind}; @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching transmute over constants that resolve to `null`. if let ExprKind::Path(ref _qpath) = arg.kind - && let Some(Constant::RawPtr(0)) = constant(cx, cx.typeck_results(), arg) + && let Some(Constant::RawPtr(0)) = ConstEvalCtxt::new(cx).eval(arg) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 83cc9f2d8df23..2fcfc71a8c768 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -48,15 +48,15 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m let inner_snippet = snippet(cx, inner.span, ".."); let suggestion = match &inner.kind { TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => { - format!("&{ltopt}({})", &inner_snippet) + format!("&{ltopt}({inner_snippet})") }, TyKind::Path(qpath) if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) .map_or(false, |bounds| bounds.len() > 1) => { - format!("&{ltopt}({})", &inner_snippet) + format!("&{ltopt}({inner_snippet})") }, - _ => format!("&{ltopt}{}", &inner_snippet), + _ => format!("&{ltopt}{inner_snippet}"), }; span_lint_and_sugg( cx, @@ -82,16 +82,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m // Returns true if given type is `Any` trait. fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool { if let TyKind::TraitObject(traits, ..) = t.kind { - return traits - .iter() - .any(|(bound, _)| { - if let Some(trait_did) = bound.trait_ref.trait_def_id() - && cx.tcx.is_diagnostic_item(sym::Any, trait_did) - { - return true; - } - false - }); + return traits.iter().any(|(bound, _)| { + if let Some(trait_did) = bound.trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Any, trait_did) + { + return true; + } + false + }); } false diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index f6c2d8d5a5ed9..d691f1878b11b 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; @@ -13,14 +13,15 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ 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( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, RC_BUFFER, hir_ty.span, "usage of `Rc<T>` when T is a buffer type", - "try", - format!("Rc<{alternate}>"), - app, + |diag| { + diag.span_suggestion(hir_ty.span, "try", format!("Rc<{alternate}>"), app); + }, ); } else { let Some(ty) = qpath_generic_tys(qpath).next() else { @@ -35,31 +36,37 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ Some(ty) => ty.span, None => return false, }; - let mut applicability = app; - span_lint_and_sugg( + span_lint_and_then( cx, RC_BUFFER, hir_ty.span, "usage of `Rc<T>` when T is a buffer type", - "try", - format!( - "Rc<[{}]>", - snippet_with_applicability(cx, inner_span, "..", &mut applicability) - ), - app, + |diag| { + let mut applicability = app; + diag.span_suggestion( + hir_ty.span, + "try", + format!( + "Rc<[{}]>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + app, + ); + }, ); return true; } } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { if let Some(alternate) = match_buffer_type(cx, qpath) { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, RC_BUFFER, hir_ty.span, "usage of `Arc<T>` when T is a buffer type", - "try", - format!("Arc<{alternate}>"), - app, + |diag| { + diag.span_suggestion(hir_ty.span, "try", format!("Arc<{alternate}>"), app); + }, ); } else if let Some(ty) = qpath_generic_tys(qpath).next() { let Some(id) = path_def_id(cx, ty) else { return false }; @@ -71,18 +78,23 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ Some(ty) => ty.span, None => return false, }; - let mut applicability = app; - span_lint_and_sugg( + span_lint_and_then( cx, RC_BUFFER, hir_ty.span, "usage of `Arc<T>` when T is a buffer type", - "try", - format!( - "Arc<[{}]>", - snippet_with_applicability(cx, inner_span, "..", &mut applicability) - ), - app, + |diag| { + let mut applicability = app; + diag.span_suggestion( + hir_ty.span, + "try", + format!( + "Arc<[{}]>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + app, + ); + }, ); return true; } diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index afc319217042f..7b13debc01ef8 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; @@ -13,14 +13,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ && let Some(id) = path_def_id(cx, arg) && cx.tcx.is_diagnostic_item(sym::Mutex, id) { - span_lint_and_help( - cx, - RC_MUTEX, - hir_ty.span, - "usage of `Rc<Mutex<_>>`", - None, - "consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead", - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, RC_MUTEX, hir_ty.span, "usage of `Rc<Mutex<_>>`", |diag| { + diag.help("consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead"); + }); return true; } diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 3ab30bf5dba7d..f51c5f74d9930 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; use clippy_utils::visitors::{for_each_expr, Descend}; @@ -129,13 +129,15 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { block.span }; - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, UNDOCUMENTED_UNSAFE_BLOCKS, span, "unsafe block missing a safety comment", - None, - "consider adding a safety comment on the preceding line", + |diag| { + diag.help("consider adding a safety comment on the preceding line"); + }, ); } @@ -145,13 +147,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) { - span_lint_and_help( + span_lint_and_then( cx, UNNECESSARY_SAFETY_COMMENT, tail.span, "expression has unnecessary safety comment", - Some(help_span), - "consider removing the safety comment", + |diag| { + diag.span_help(help_span, "consider removing the safety comment"); + }, ); } } @@ -168,13 +171,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) { - span_lint_and_help( + span_lint_and_then( cx, UNNECESSARY_SAFETY_COMMENT, stmt.span, "statement has unnecessary safety comment", - Some(help_span), - "consider removing the safety comment", + |diag| { + diag.span_help(help_span, "consider removing the safety comment"); + }, ); } } @@ -210,13 +214,15 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { item.span }; - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, UNDOCUMENTED_UNSAFE_BLOCKS, span, "unsafe impl missing a safety comment", - None, - "consider adding a safety comment on the preceding line", + |diag| { + diag.help("consider adding a safety comment on the preceding line"); + }, ); } }, @@ -225,13 +231,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { let (span, help_span) = mk_spans(pos); - span_lint_and_help( + span_lint_and_then( cx, UNNECESSARY_SAFETY_COMMENT, span, "impl has unnecessary safety comment", - Some(help_span), - "consider removing the safety comment", + |diag| { + diag.span_help(help_span, "consider removing the safety comment"); + }, ); } }, @@ -246,13 +253,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { ) { let (span, help_span) = mk_spans(pos); - span_lint_and_help( + span_lint_and_then( cx, UNNECESSARY_SAFETY_COMMENT, span, format!("{} has unnecessary safety comment", item.kind.descr()), - Some(help_span), - "consider removing the safety comment", + |diag| { + diag.span_help(help_span, "consider removing the safety comment"); + }, ); } } @@ -263,13 +271,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { let (span, help_span) = mk_spans(pos); - span_lint_and_help( + span_lint_and_then( cx, UNNECESSARY_SAFETY_COMMENT, span, format!("{} has unnecessary safety comment", item.kind.descr()), - Some(help_span), - "consider removing the safety comment", + |diag| { + diag.span_help(help_span, "consider removing the safety comment"); + }, ); } }, diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d42697b31d1f2..e1fc644e4ceeb 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use clippy_utils::macros::span_is_local; use clippy_utils::source::snippet; @@ -105,45 +105,51 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) { - span_lint_and_sugg( - cx, - INVISIBLE_CHARACTERS, - span, - "invisible character detected", - "consider replacing the string with", - string - .replace('\u{200B}', "\\u{200B}") - .replace('\u{ad}', "\\u{AD}") - .replace('\u{2060}', "\\u{2060}"), - Applicability::MachineApplicable, - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, INVISIBLE_CHARACTERS, span, "invisible character detected", |diag| { + diag.span_suggestion( + span, + "consider replacing the string with", + string + .replace('\u{200B}', "\\u{200B}") + .replace('\u{ad}', "\\u{AD}") + .replace('\u{2060}', "\\u{2060}"), + Applicability::MachineApplicable, + ); + }); } if string.chars().any(|c| c as u32 > 0x7F) { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, NON_ASCII_LITERAL, span, "literal non-ASCII character detected", - "consider replacing the string with", - if is_lint_allowed(cx, UNICODE_NOT_NFC, id) { - escape(string.chars()) - } else { - escape(string.nfc()) + |diag| { + diag.span_suggestion( + span, + "consider replacing the string with", + if is_lint_allowed(cx, UNICODE_NOT_NFC, id) { + escape(string.chars()) + } else { + escape(string.nfc()) + }, + Applicability::MachineApplicable, + ); }, - Applicability::MachineApplicable, ); } if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) { - span_lint_and_sugg( - cx, - UNICODE_NOT_NFC, - span, - "non-NFC Unicode sequence detected", - "consider replacing the string with", - string.nfc().collect::<String>(), - Applicability::MachineApplicable, - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, UNICODE_NOT_NFC, span, "non-NFC Unicode sequence detected", |diag| { + diag.span_suggestion( + span, + "consider replacing the string with", + string.nfc().collect::<String>(), + Applicability::MachineApplicable, + ); + }); } } diff --git a/clippy_lints/src/unused_result_ok.rs b/clippy_lints/src/unused_result_ok.rs new file mode 100644 index 0000000000000..297288db0a56b --- /dev/null +++ b/clippy_lints/src/unused_result_ok.rs @@ -0,0 +1,59 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::{ExprKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `Result::ok()` without using the returned `Option`. + /// + /// ### Why is this bad? + /// Using `Result::ok()` may look like the result is checked like `unwrap` or `expect` would do + /// but it only silences the warning caused by `#[must_use]` on the `Result`. + /// + /// ### Example + /// ```no_run + /// # fn some_function() -> Result<(), ()> { Ok(()) } + /// some_function().ok(); + /// ``` + /// Use instead: + /// ```no_run + /// # fn some_function() -> Result<(), ()> { Ok(()) } + /// let _ = some_function(); + /// ``` + #[clippy::version = "1.70.0"] + pub UNUSED_RESULT_OK, + restriction, + "Use of `.ok()` to silence `Result`'s `#[must_use]` is misleading. Use `let _ =` instead." +} +declare_lint_pass!(UnusedResultOk => [UNUSED_RESULT_OK]); + +impl LateLintPass<'_> for UnusedResultOk { + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { + if let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(ok_path, recv, [], ..) = expr.kind //check is expr.ok() has type Result<T,E>.ok(, _) + && ok_path.ident.as_str() == "ok" + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + && !in_external_macro(cx.sess(), stmt.span) + { + let ctxt = expr.span.ctxt(); + let mut applicability = Applicability::MaybeIncorrect; + let snippet = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0; + let sugg = format!("let _ = {snippet}"); + span_lint_and_sugg( + cx, + UNUSED_RESULT_OK, + expr.span, + "ignoring a result with `.ok()` is misleading", + "consider using `let _ =` and removing the call to `.ok()` instead", + sugg, + applicability, + ); + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index b017a6bf665c5..7c2e23995c1d3 100644 --- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; @@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if let ExprKind::Call(func, [arg]) = &expr.kind && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind() && match_def_path(cx, *def_id, &paths::SYMBOL_INTERN) - && let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg) + && let Some(Constant::Str(arg)) = ConstEvalCtxt::new(cx).eval_simple(arg) && let value = Symbol::intern(&arg).as_u32() && let Some(&def_id) = self.symbol_map.get(&value) { @@ -199,7 +199,7 @@ impl InterningDefinedSymbol { }); } // is a string constant - if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { + if let Some(Constant::Str(s)) = ConstEvalCtxt::new(cx).eval_simple(expr) { let value = Symbol::intern(&s).as_u32(); // ...which matches a symbol constant if let Some(&def_id) = self.symbol_map.get(&value) { diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 980437259c3e9..0ffcb433481e4 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; @@ -32,9 +32,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); if mod_name.as_str() == "paths" && let hir::ItemKind::Const(.., body_id) = item.kind - && let body = cx.tcx.hir().body(body_id) - && let typeck_results = cx.tcx.typeck_body(body_id) - && let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value) + && let Some(Constant::Vec(path)) = + ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(item.owner_id), cx.tcx.typeck(item.owner_id)) + .eval_simple(cx.tcx.hir().body(body_id).value) && let Some(path) = path .iter() .map(|x| { 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 index 84f84781e7126..df342e48d637f 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -1,4 +1,3 @@ -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}; @@ -87,82 +86,32 @@ declare_clippy_lint! { "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<Symbol, Span>, registered_lints: FxHashSet<Symbol>, } -impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]); +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]); 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()) - { + if is_lint_allowed(cx, DEFAULT_LINT, 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 { + if is_lint_ref_type(cx, 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; + let fields = if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind + && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind + { + struct_fields } else { return; - } + }; let field = fields .iter() @@ -175,25 +124,15 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { }) = 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" { + if sym_str == "default lint description" { span_lint( cx, - DEFAULT_DEPRECATION_REASON, + DEFAULT_LINT, item.span, - format!("the lint `{}` has the default deprecation reason", item.ident.name), + format!("the lint `{}` has the default lint description", item.ident.name), ); } + self.declared_lints.insert(item.ident.name, item.span); } } } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 1c149f2045629..57f45aa3e4851 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -7,13 +7,12 @@ //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such //! a simple mistake) -use crate::renamed_lints::RENAMED_LINTS; use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}; use clippy_config::{get_configuration_metadata, ClippyConfiguration}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; -use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths}; +use clippy_utils::{last_path_segment, match_function_call, match_path, paths}; use itertools::Itertools; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; @@ -85,11 +84,6 @@ const SUGGESTION_DIAG_METHODS: [(&str, bool); 9] = [ ("tool_only_multipart_suggestion", true), ("span_suggestions", true), ]; -const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [ - &["clippy_utils", "diagnostics", "multispan_sugg"], - &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"], -]; -const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"]; /// The index of the applicability name of `paths::APPLICABILITY_VALUES` const APPLICABILITY_NAME_INDEX: usize = 2; @@ -165,9 +159,9 @@ impl MetadataCollector { fn get_lint_configs(&self, lint_name: &str) -> Option<String> { self.config .iter() - .filter(|config| config.lints.iter().any(|lint| lint == lint_name)) + .filter(|config| config.lints.iter().any(|&lint| lint == lint_name)) .map(ToString::to_string) - .reduce(|acc, x| acc + &x) + .reduce(|acc, x| acc + "\n\n" + &x) .map(|configurations| { format!( r#" @@ -216,6 +210,13 @@ impl Drop for MetadataCollector { let mut applicability_info = std::mem::take(&mut self.applicability_info); + // Add deprecated lints + self.lints.extend( + crate::deprecated_lints::DEPRECATED + .iter() + .zip(crate::deprecated_lints::DEPRECATED_VERSION) + .filter_map(|((lint, reason), version)| LintMetadata::new_deprecated(lint, reason, version)), + ); // Mapping the final data let mut lints = std::mem::take(&mut self.lints).into_sorted_vec(); for x in &mut lints { @@ -261,7 +262,7 @@ Please use that command to update the file and do not edit it by hand. #[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] struct LintMetadata { id: String, - id_span: SerializableSpan, + id_span: Option<SerializableSpan>, group: String, level: String, docs: String, @@ -285,7 +286,7 @@ impl LintMetadata { ) -> Self { Self { id, - id_span, + id_span: Some(id_span), group, level: level.to_string(), version, @@ -294,6 +295,29 @@ impl LintMetadata { former_ids: BTreeSet::new(), } } + + fn new_deprecated(name: &str, reason: &str, version: &str) -> Option<Self> { + // The reason starts with a lowercase letter and end without a period. + // This needs to be fixed for the website. + let mut reason = reason.to_owned(); + if let Some(reason) = reason.get_mut(0..1) { + reason.make_ascii_uppercase(); + } + name.strip_prefix("clippy::").map(|name| Self { + id: name.into(), + id_span: None, + group: DEPRECATED_LINT_GROUP_STR.into(), + level: DEPRECATED_LINT_LEVEL.into(), + version: version.into(), + docs: format!( + "### What it does\n\n\ + Nothing. This lint has been deprecated\n\n\ + ### Deprecation reason\n\n{reason}.\n", + ), + applicability: None, + former_ids: BTreeSet::new(), + }) + } } fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) { @@ -564,24 +588,6 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { raw_docs, )); } - - if is_deprecated_lint(cx, ty) - // disallow check - && let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase() - // Metadata the little we can get from a deprecated lint - && let Some(raw_docs) = extract_attr_docs_or_lint(cx, item) - { - let version = get_lint_version(cx, item); - - self.lints.push(LintMetadata::new( - lint_name, - SerializableSpan::from_item(cx, item), - DEPRECATED_LINT_GROUP_STR.to_string(), - DEPRECATED_LINT_LEVEL, - version, - raw_docs, - )); - } } } @@ -684,6 +690,11 @@ fn cleanup_docs(docs_collection: &Vec<String>) -> String { .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic")) // if no language is present, fill in "rust" .unwrap_or("rust"); + let len_diff = line.len() - line.trim_start().len(); + if len_diff != 0 { + // We put back the indentation. + docs.push_str(&line[..len_diff]); + } docs.push_str("```"); docs.push_str(lang); @@ -766,16 +777,6 @@ fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> { .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level)) } -pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { - if let hir::TyKind::Path(ref path) = ty.kind { - if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) { - return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE); - } - } - - false -} - fn collect_renames(lints: &mut Vec<LintMetadata>) { for lint in lints { let mut collected = String::new(); @@ -783,7 +784,7 @@ fn collect_renames(lints: &mut Vec<LintMetadata>) { loop { if let Some(lint_name) = names.pop() { - for (k, v) in RENAMED_LINTS { + for (k, v) in crate::deprecated_lints::RENAMED { if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX) && name == lint_name && let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX) @@ -1055,33 +1056,21 @@ impl<'a, 'hir> Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> { return; } - match &expr.kind { - ExprKind::Call(fn_expr, _args) => { - let found_function = SUGGESTION_FUNCTIONS - .iter() - .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some()); - if found_function { - // These functions are all multi part suggestions - self.add_single_span_suggestion(); - } - }, - ExprKind::MethodCall(path, recv, _, _arg_span) => { - let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv)); - if match_type(self.cx, self_ty, &paths::DIAG) { - let called_method = path.ident.name.as_str().to_string(); - for (method_name, is_multi_part) in &SUGGESTION_DIAG_METHODS { - if *method_name == called_method { - if *is_multi_part { - self.add_multi_part_suggestion(); - } else { - self.add_single_span_suggestion(); - } - break; + if let ExprKind::MethodCall(path, recv, _, _arg_span) = &expr.kind { + let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv)); + if match_type(self.cx, self_ty, &paths::DIAG) { + let called_method = path.ident.name.as_str().to_string(); + for (method_name, is_multi_part) in &SUGGESTION_DIAG_METHODS { + if *method_name == called_method { + if *is_multi_part { + self.add_multi_part_suggestion(); + } else { + self.add_single_span_suggestion(); } + break; } } - }, - _ => {}, + } } intravisit::walk_expr(self, expr); diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index a831234906bfc..228db14d1b7c3 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_copy; @@ -159,7 +159,7 @@ impl UselessVec { let snippet = match *vec_args { higher::VecArgs::Repeat(elem, len) => { - if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) { + if let Some(Constant::Int(len_constant)) = ConstEvalCtxt::new(cx).eval(len) { // vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also if !is_copy(cx, cx.typeck_results().expr_ty(elem)) { return; diff --git a/clippy_lints/src/visibility.rs b/clippy_lints/src/visibility.rs index 11dcceca7abb1..63f3a5d7f8305 100644 --- a/clippy_lints/src/visibility.rs +++ b/clippy_lints/src/visibility.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Item, VisibilityKind}; use rustc_errors::Applicability; @@ -85,14 +85,19 @@ impl EarlyLintPass for Visibility { if **path == kw::SelfLower && let Some(false) = is_from_proc_macro(cx, item.vis.span) { - span_lint_and_sugg( + span_lint_and_then( cx, NEEDLESS_PUB_SELF, item.vis.span, format!("unnecessary `pub({}self)`", if *shorthand { "" } else { "in " }), - "remove it", - String::new(), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion_hidden( + item.vis.span, + "remove it", + String::new(), + Applicability::MachineApplicable, + ); + }, ); } @@ -101,14 +106,20 @@ impl EarlyLintPass for Visibility { && let [.., last] = &*path.segments && let Some(false) = is_from_proc_macro(cx, item.vis.span) { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, PUB_WITHOUT_SHORTHAND, item.vis.span, "usage of `pub` with `in`", - "remove it", - format!("pub({})", last.ident), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + item.vis.span, + "remove it", + format!("pub({})", last.ident), + Applicability::MachineApplicable, + ); + }, ); } @@ -116,14 +127,20 @@ impl EarlyLintPass for Visibility { && let [.., last] = &*path.segments && let Some(false) = is_from_proc_macro(cx, item.vis.span) { - span_lint_and_sugg( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, PUB_WITH_SHORTHAND, item.vis.span, "usage of `pub` without `in`", - "add it", - format!("pub(in {})", last.ident), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + item.vis.span, + "add it", + format!("pub(in {})", last.ident), + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 60d8a13d3599d..5eb207a0aedb0 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -36,8 +36,9 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { // TODO - constant_simple does not fold many operations involving floats. // That's probably fine for this lint - it's pretty unlikely that someone would // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. - && let Some(lhs_value) = constant_simple(cx, cx.typeck_results(), left) - && let Some(rhs_value) = constant_simple(cx, cx.typeck_results(), right) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(lhs_value) = ecx.eval_simple(left) + && let Some(rhs_value) = ecx.eval_simple(right) // FIXME(f16_f128): add these types when eq is available on all platforms && (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value) && (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value) diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index f0c64fdd573ca..9fefd94d339be 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -12,9 +12,6 @@ rustc-semver = "1.1" # FIXME(f16_f128): remove when no longer needed for parsing rustc_apfloat = "0.2.0" -[features] -deny-warnings = ["clippy_config/deny-warnings"] - [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] rustc_private = true diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index de6ccfe476fb0..e907e4058e5a2 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -14,12 +14,13 @@ use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{alloc_range, Scalar}; use rustc_middle::mir::ConstValue; -use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy}; +use rustc_middle::ty::{self, FloatTy, IntTy, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::def_id::DefId; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::Ident; use rustc_span::{sym, SyntaxContext}; use rustc_target::abi::Size; +use std::cell::Cell; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; use std::iter; @@ -263,10 +264,10 @@ impl<'tcx> Constant<'tcx> { } /// Returns the integer value or `None` if `self` or `val_type` is not integer type. - pub fn int_value(&self, cx: &LateContext<'_>, val_type: Ty<'_>) -> Option<FullInt> { + pub fn int_value(&self, tcx: TyCtxt<'_>, val_type: Ty<'_>) -> Option<FullInt> { if let Constant::Int(const_int) = *self { match *val_type.kind() { - ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))), + ty::Int(ity) => Some(FullInt::S(sext(tcx, const_int, ity))), ty::Uint(_) => Some(FullInt::U(const_int)), _ => None, } @@ -322,6 +323,7 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option<Ty<'tcx>>) -> Constan } /// The source of a constant value. +#[derive(Clone, Copy)] pub enum ConstantSource { /// The value is determined solely from the expression. Local, @@ -331,54 +333,11 @@ pub enum ConstantSource { CoreConstant, } impl ConstantSource { - pub fn is_local(&self) -> bool { + pub fn is_local(self) -> bool { matches!(self, Self::Local) } } -/// Attempts to check whether the expression is a constant representing an empty slice, str, array, -/// etc… -pub fn constant_is_empty(lcx: &LateContext<'_>, e: &Expr<'_>) -> Option<bool> { - ConstEvalLateContext::new(lcx, lcx.typeck_results()).expr_is_empty(e) -} - -/// Attempts to evaluate the expression as a constant. -pub fn constant<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option<Constant<'tcx>> { - ConstEvalLateContext::new(lcx, typeck_results).expr(e) -} - -/// Attempts to evaluate the expression as a constant. -pub fn constant_with_source<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option<(Constant<'tcx>, ConstantSource)> { - let mut ctxt = ConstEvalLateContext::new(lcx, typeck_results); - let res = ctxt.expr(e); - res.map(|x| (x, ctxt.source)) -} - -/// Attempts to evaluate an expression only if its value is not dependent on other items. -pub fn constant_simple<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option<Constant<'tcx>> { - constant_with_source(lcx, typeck_results, e).and_then(|(c, s)| s.is_local().then_some(c)) -} - -pub fn constant_full_int<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option<FullInt> { - constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e)) -} - #[derive(Copy, Clone, Debug, Eq)] pub enum FullInt { S(i128), @@ -417,44 +376,87 @@ impl Ord for FullInt { } } -pub struct ConstEvalLateContext<'a, 'tcx> { - lcx: &'a LateContext<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - param_env: ty::ParamEnv<'tcx>, - source: ConstantSource, - args: GenericArgsRef<'tcx>, +/// The context required to evaluate a constant expression. +/// +/// This is currently limited to constant folding and reading the value of named constants. +pub struct ConstEvalCtxt<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + typeck: &'tcx TypeckResults<'tcx>, + source: Cell<ConstantSource>, } -impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { - pub fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self { +impl<'tcx> ConstEvalCtxt<'tcx> { + /// Creates the evaluation context from the lint context. This requires the lint context to be + /// in a body (i.e. `cx.enclosing_body.is_some()`). + pub fn new(cx: &LateContext<'tcx>) -> Self { + Self { + tcx: cx.tcx, + param_env: cx.param_env, + typeck: cx.typeck_results(), + source: Cell::new(ConstantSource::Local), + } + } + + /// Creates an evaluation context. + pub fn with_env(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>) -> Self { Self { - lcx, - typeck_results, - param_env: lcx.param_env, - source: ConstantSource::Local, - args: List::empty(), + tcx, + param_env, + typeck, + source: Cell::new(ConstantSource::Local), + } + } + + /// Attempts to evaluate the expression and returns both the value and whether it's dependant on + /// other items. + pub fn eval_with_source(&self, e: &Expr<'_>) -> Option<(Constant<'tcx>, ConstantSource)> { + self.source.set(ConstantSource::Local); + self.expr(e).map(|c| (c, self.source.get())) + } + + /// Attempts to evaluate the expression. + pub fn eval(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> { + self.expr(e) + } + + /// Attempts to evaluate the expression without accessing other items. + pub fn eval_simple(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> { + match self.eval_with_source(e) { + Some((x, ConstantSource::Local)) => Some(x), + _ => None, + } + } + + /// Attempts to evaluate the expression as an integer without accessing other items. + pub fn eval_full_int(&self, e: &Expr<'_>) -> Option<FullInt> { + match self.eval_with_source(e) { + Some((x, ConstantSource::Local)) => x.int_value(self.tcx, self.typeck.expr_ty(e)), + _ => None, } } /// Simple constant folding: Insert an expression, get a constant or none. - pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> { + fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> { match e.kind { - ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value), + ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), ExprKind::Path(ref qpath) => { - let is_core_crate = if let Some(def_id) = self.lcx.qpath_res(qpath, e.hir_id()).opt_def_id() { - self.lcx.tcx.crate_name(def_id.krate) == sym::core + let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, e.hir_id()).opt_def_id() { + self.tcx.crate_name(def_id.krate) == sym::core } else { false }; - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { - let result = mir_to_const(this.lcx, result)?; + self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { + let result = mir_to_const(self_.tcx, result)?; // If source is already Constant we wouldn't want to override it with CoreConstant - this.source = if is_core_crate && !matches!(this.source, ConstantSource::Constant) { - ConstantSource::CoreConstant - } else { - ConstantSource::Constant - }; + self_.source.set( + if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { + ConstantSource::CoreConstant + } else { + ConstantSource::Constant + }, + ); Some(result) }) }, @@ -463,21 +465,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { if is_direct_expn_of(e.span, "cfg").is_some() { None } else { - Some(lit_to_mir_constant(&lit.node, self.typeck_results.expr_ty_opt(e))) + Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) } }, ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec), ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple), ExprKind::Repeat(value, _) => { - let n = match self.typeck_results.expr_ty(e).kind() { - ty::Array(_, n) => n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)?, + let n = match self.typeck.expr_ty(e).kind() { + ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?, _ => span_bug!(e.span, "typeck error"), }; self.expr(value).map(|v| Constant::Repeat(Box::new(v), n)) }, ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op { - UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)), - UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)), + UnOp::Not => self.constant_not(&o, self.typeck.expr_ty(e)), + UnOp::Neg => self.constant_negate(&o, self.typeck.expr_ty(e)), UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), @@ -486,21 +488,16 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { // We only handle a few const functions for now. if args.is_empty() && let ExprKind::Path(qpath) = &callee.kind - && let res = self.typeck_results.qpath_res(qpath, callee.hir_id) - && let Some(def_id) = res.opt_def_id() - && let def_path = self.lcx.get_def_path(def_id) - && let def_path = def_path.iter().take(4).map(Symbol::as_str).collect::<Vec<_>>() - && let ["core", "num", int_impl, "max_value"] = *def_path + && let Some(did) = self.typeck.qpath_res(qpath, callee.hir_id).opt_def_id() { - let value = match int_impl { - "<impl i8>" => i8::MAX as u128, - "<impl i16>" => i16::MAX as u128, - "<impl i32>" => i32::MAX as u128, - "<impl i64>" => i64::MAX as u128, - "<impl i128>" => i128::MAX as u128, - _ => return None, - }; - Some(Constant::Int(value)) + match self.tcx.get_diagnostic_name(did) { + Some(sym::i8_legacy_fn_max_value) => Some(Constant::Int(i8::MAX as u128)), + Some(sym::i16_legacy_fn_max_value) => Some(Constant::Int(i16::MAX as u128)), + Some(sym::i32_legacy_fn_max_value) => Some(Constant::Int(i32::MAX as u128)), + Some(sym::i64_legacy_fn_max_value) => Some(Constant::Int(i64::MAX as u128)), + Some(sym::i128_legacy_fn_max_value) => Some(Constant::Int(i128::MAX as u128)), + _ => None, + } } else { None } @@ -512,9 +509,9 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { if let Some(Constant::Adt(constant)) = &self.expr(local_expr) && let ty::Adt(adt_def, _) = constant.ty().kind() && adt_def.is_struct() - && let Some(desired_field) = field_of_struct(*adt_def, self.lcx, *constant, field) + && let Some(desired_field) = field_of_struct(*adt_def, self.tcx, *constant, field) { - mir_to_const(self.lcx, desired_field) + mir_to_const(self.tcx, desired_field) } else { result } @@ -526,21 +523,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { /// Simple constant folding to determine if an expression is an empty slice, str, array, … /// `None` will be returned if the constness cannot be determined, or if the resolution /// leaves the local crate. - pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> { + pub fn eval_is_empty(&self, e: &Expr<'_>) -> Option<bool> { match e.kind { - ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), - ExprKind::DropTemps(e) => self.expr_is_empty(e), + ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir().body(body).value), + ExprKind::DropTemps(e) => self.eval_is_empty(e), ExprKind::Path(ref qpath) => { if !self - .typeck_results + .typeck .qpath_res(qpath, e.hir_id) .opt_def_id() .is_some_and(DefId::is_local) { return None; } - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { - mir_is_empty(this.lcx, result) + self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { + mir_is_empty(self_.tcx, result) }) }, ExprKind::Lit(lit) => { @@ -556,8 +553,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { }, ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()), ExprKind::Repeat(..) => { - if let ty::Array(_, n) = self.typeck_results.expr_ty(e).kind() { - Some(n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)? == 0) + if let ty::Array(_, n) = self.typeck.expr_ty(e).kind() { + Some(n.try_eval_target_usize(self.tcx, self.param_env)? == 0) } else { span_bug!(e.span, "typeck error"); } @@ -574,8 +571,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { Int(value) => { let value = !value; match *ty.kind() { - ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))), - ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))), + ty::Int(ity) => Some(Int(unsext(self.tcx, value as i128, ity))), + ty::Uint(ity) => Some(Int(clip(self.tcx, value, ity))), _ => None, } }, @@ -590,7 +587,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let ty::Int(ity) = *ty.kind() else { return None }; let (min, _) = ity.min_max()?; // sign extend - let value = sext(self.lcx.tcx, value, ity); + let value = sext(self.tcx, value, ity); // Applying unary - to the most negative value of any signed integer type panics. if value == min { @@ -599,7 +596,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let value = value.checked_neg()?; // clear unused bits - Some(Int(unsext(self.lcx.tcx, value, ity))) + Some(Int(unsext(self.tcx, value, ity))) }, F32(f) => Some(F32(-f)), F64(f) => Some(F64(-f)), @@ -609,21 +606,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { /// Create `Some(Vec![..])` of all constants, unless there is any /// non-constant part. - fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> { + fn multi(&self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> { vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>() } /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it. - fn fetch_path_and_apply<T, F>(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T> + fn fetch_path_and_apply<T, F>(&self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T> where - F: FnOnce(&mut Self, mir::Const<'tcx>) -> Option<T>, + F: FnOnce(&Self, mir::Const<'tcx>) -> Option<T>, { - let res = self.typeck_results.qpath_res(qpath, id); + let res = self.typeck.qpath_res(qpath, id); match res { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { // Check if this constant is based on `cfg!(..)`, // which is NOT constant for our purposes. - if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) + if let Some(node) = self.tcx.hir().get_if_local(def_id) && let Node::Item(Item { kind: ItemKind::Const(.., body_id), .. @@ -632,20 +629,14 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { kind: ExprKind::Lit(_), span, .. - }) = self.lcx.tcx.hir_node(body_id.hir_id) + }) = self.tcx.hir_node(body_id.hir_id) && is_direct_expn_of(*span, "cfg").is_some() { return None; } - let args = self.typeck_results.node_args(id); - let args = if self.args.is_empty() { - args - } else { - EarlyBinder::bind(args).instantiate(self.lcx.tcx, self.args) - }; + let args = self.typeck.node_args(id); let result = self - .lcx .tcx .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) .ok() @@ -656,7 +647,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } - fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> { + fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> { let lhs = self.expr(lhs); let index = self.expr(index); @@ -685,8 +676,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } - /// A block can only yield a constant if it only has one constant expression. - fn block(&mut self, block: &Block<'_>) -> Option<Constant<'tcx>> { + /// A block can only yield a constant if it has exactly one constant expression. + fn block(&self, block: &Block<'_>) -> Option<Constant<'tcx>> { if block.stmts.is_empty() && let Some(expr) = block.expr { @@ -696,7 +687,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) && let expr_lo = expr_span.lo() && expr_lo >= span.lo - && let Some(src) = (span.lo..expr_lo).get_source_text(self.lcx) + && let Some(src) = (span.lo..expr_lo).get_source_text(&self.tcx) && let Some(src) = src.as_str() { use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace}; @@ -705,11 +696,11 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)) .eq([OpenBrace]) { - self.source = ConstantSource::Constant; + self.source.set(ConstantSource::Constant); } } else { // Unable to access the source. Assume a non-local dependency. - self.source = ConstantSource::Constant; + self.source.set(ConstantSource::Constant); } } @@ -719,7 +710,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } - fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> { + fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> { if let Some(Constant::Bool(b)) = self.expr(cond) { if b { self.expr(then) @@ -731,16 +722,16 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } - fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> { + fn binop(&self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { - (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() { + (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck.expr_ty_opt(left)?.kind() { ty::Int(ity) => { let (ty_min_value, _) = ity.min_max()?; let bits = ity.bits(); - let l = sext(self.lcx.tcx, l, ity); - let r = sext(self.lcx.tcx, r, ity); + let l = sext(self.tcx, l, ity); + let r = sext(self.tcx, r, ity); // Using / or %, where the left-hand argument is the smallest integer of a signed integer type and // the right-hand argument is -1 always panics, even with overflow-checks disabled @@ -751,7 +742,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { return None; } - let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity)); + let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity)); match op.node { // When +, * or binary - create a value greater than the maximum value, or less than // the minimum value that can be stored, it panics. @@ -845,7 +836,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } -pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> { +pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> { let mir::Const::Val(val, _) = result else { // We only work on evaluated consts. return None; @@ -863,13 +854,13 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> _ => None, }, (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => { - let data = val.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; + let data = val.try_get_slice_bytes_for_diagnostics(tcx)?; String::from_utf8(data.to_owned()).ok().map(Constant::Str) }, (_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)), (ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => { - let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); - let len = len.try_to_target_usize(lcx.tcx)?; + let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner(); + let len = len.try_to_target_usize(tcx)?; let ty::Float(flt) = sub_type.kind() else { return None; }; @@ -877,7 +868,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> let mut res = Vec::new(); for idx in 0..len { let range = alloc_range(offset + size * idx, size); - let val = alloc.read_scalar(&lcx.tcx, range, /* read_provenance */ false).ok()?; + let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?; res.push(match flt { FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)), FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)), @@ -891,7 +882,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> } } -fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<bool> { +fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<bool> { let mir::Const::Val(val, _) = result else { // We only work on evaluated consts. return None; @@ -902,26 +893,26 @@ fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Opti if let ConstValue::Indirect { alloc_id, offset } = val { // Get the length from the slice, using the same formula as // [`ConstValue::try_get_slice_bytes_for_diagnostics`]. - let a = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); - let ptr_size = lcx.tcx.data_layout.pointer_size; + let a = tcx.global_alloc(alloc_id).unwrap_memory().inner(); + let ptr_size = tcx.data_layout.pointer_size; if a.size() < offset + 2 * ptr_size { // (partially) dangling reference return None; } let len = a - .read_scalar(&lcx.tcx, alloc_range(offset + ptr_size, ptr_size), false) + .read_scalar(&tcx, alloc_range(offset + ptr_size, ptr_size), false) .ok()? - .to_target_usize(&lcx.tcx) + .to_target_usize(&tcx) .ok()?; Some(len == 0) } else { None } }, - ty::Array(_, len) => Some(len.try_to_target_usize(lcx.tcx)? == 0), + ty::Array(_, len) => Some(len.try_to_target_usize(tcx)? == 0), _ => None, }, - (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(lcx.tcx)? == 0), + (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(tcx)? == 0), (ConstValue::ZeroSized, _) => Some(true), _ => None, } @@ -929,12 +920,12 @@ fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Opti fn field_of_struct<'tcx>( adt_def: ty::AdtDef<'tcx>, - lcx: &LateContext<'tcx>, + tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>, field: &Ident, ) -> Option<mir::Const<'tcx>> { if let mir::Const::Val(result, ty) = result - && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_user_output(result, ty) + && let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(result, ty) && let Some(dc_variant) = dc.variant && let Some(variant) = adt_def.variants().get(dc_variant) && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name) diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 0641d37cd9a67..4877fb65d37d7 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -330,32 +330,3 @@ pub fn span_lint_and_sugg<T: LintContext>( diag.span_suggestion(sp, help.into(), sugg, applicability); }); } - -/// Create a suggestion made from several `span → replacement`. -/// -/// Note: in the JSON format (used by `compiletest_rs`), the help message will -/// appear once per -/// replacement. In human-readable format though, it only appears once before -/// the whole suggestion. -pub fn multispan_sugg<I>(diag: &mut Diag<'_, ()>, help_msg: impl Into<SubdiagMessage>, sugg: I) -where - I: IntoIterator<Item = (Span, String)>, -{ - multispan_sugg_with_applicability(diag, help_msg, Applicability::Unspecified, sugg); -} - -/// Create a suggestion made from several `span → replacement`. -/// -/// rustfix currently doesn't support the automatic application of suggestions with -/// multiple spans. This is tracked in issue [rustfix#141](https://github.com/rust-lang/rustfix/issues/141). -/// Suggestions with multiple spans will be silently ignored. -pub fn multispan_sugg_with_applicability<I>( - diag: &mut Diag<'_, ()>, - help_msg: impl Into<SubdiagMessage>, - applicability: Applicability, - sugg: I, -) where - I: IntoIterator<Item = (Span, String)>, -{ - diag.multipart_suggestion(help_msg.into(), sugg.into_iter().collect(), applicability); -} diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 6c40029a9de70..a6dd12a28c5bc 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -9,7 +9,7 @@ //! - or-fun-call //! - option-if-let-else -use crate::consts::{constant, FullInt}; +use crate::consts::{ConstEvalCtxt, FullInt}; use crate::ty::{all_predicates_of, is_copy}; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; @@ -206,7 +206,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, // `-i32::MIN` panics with overflow checks - ExprKind::Unary(UnOp::Neg, right) if constant(self.cx, self.cx.typeck_results(), right).is_none() => { + ExprKind::Unary(UnOp::Neg, right) if ConstEvalCtxt::new(self.cx).eval(right).is_none() => { self.eagerness |= NoChange; }, @@ -232,7 +232,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Thus, we would realistically only delay the lint. ExprKind::Binary(op, _, right) if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr) - && constant(self.cx, self.cx.typeck_results(), right).is_none() => + && ConstEvalCtxt::new(self.cx).eval(right).is_none() => { self.eagerness |= NoChange; }, @@ -240,9 +240,9 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Div | BinOpKind::Rem) && let right_ty = self.cx.typeck_results().expr_ty(right) - && let left = constant(self.cx, self.cx.typeck_results(), left) - && let right = constant(self.cx, self.cx.typeck_results(), right) - .and_then(|c| c.int_value(self.cx, right_ty)) + && let ecx = ConstEvalCtxt::new(self.cx) + && let left = ecx.eval(left) + && let right = ecx.eval(right).and_then(|c| c.int_value(self.cx.tcx, right_ty)) && matches!( (left, right), // `1 / x`: x might be zero @@ -261,8 +261,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul) && !self.cx.typeck_results().expr_ty(e).is_floating_point() - && (constant(self.cx, self.cx.typeck_results(), left).is_none() - || constant(self.cx, self.cx.typeck_results(), right).is_none()) => + && let ecx = ConstEvalCtxt::new(self.cx) + && (ecx.eval(left).is_none() || ecx.eval(right).is_none()) => { self.eagerness |= NoChange; }, diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 277ba8427e054..8970b4d122984 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -2,7 +2,7 @@ #![deny(clippy::missing_docs_in_private_items)] -use crate::consts::{constant_simple, Constant}; +use crate::consts::{ConstEvalCtxt, Constant}; use crate::ty::is_type_diagnostic_item; use crate::{is_expn_of, match_def_path, paths}; @@ -25,6 +25,8 @@ pub struct ForLoop<'tcx> { pub loop_id: HirId, /// entire `for` loop span pub span: Span, + /// label + pub label: Option<ast::Label>, } impl<'tcx> ForLoop<'tcx> { @@ -33,7 +35,7 @@ impl<'tcx> ForLoop<'tcx> { if let ExprKind::DropTemps(e) = expr.kind && let ExprKind::Match(iterexpr, [arm], MatchSource::ForLoopDesugar) = e.kind && let ExprKind::Call(_, [arg]) = iterexpr.kind - && let ExprKind::Loop(block, ..) = arm.body.kind + && let ExprKind::Loop(block, label, ..) = arm.body.kind && let [stmt] = block.stmts && let hir::StmtKind::Expr(e) = stmt.kind && let ExprKind::Match(_, [_, some_arm], _) = e.kind @@ -45,6 +47,7 @@ impl<'tcx> ForLoop<'tcx> { body: some_arm.body, loop_id: arm.body.hir_id, span: expr.span.ctxt().outer_expn_data().call_site, + label, }); } None @@ -367,6 +370,7 @@ pub struct WhileLet<'hir> { pub let_expr: &'hir Expr<'hir>, /// `while let` loop body pub if_then: &'hir Expr<'hir>, + pub label: Option<ast::Label>, /// `while let PAT = EXPR` /// ^^^^^^^^^^^^^^ pub let_span: Span, @@ -399,7 +403,7 @@ impl<'hir> WhileLet<'hir> { }), .. }, - _, + label, LoopSource::While, _, ) = expr.kind @@ -408,6 +412,7 @@ impl<'hir> WhileLet<'hir> { let_pat, let_expr, if_then, + label, let_span, }); } @@ -466,7 +471,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::Default); } else if name.ident.name.as_str() == "with_capacity" { let arg = args.first()?; - return match constant_simple(cx, cx.typeck_results(), arg) { + return match ConstEvalCtxt::new(cx).eval_simple(arg) { Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), }; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 28178a61a9323..f325e4eaf1544 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,4 +1,4 @@ -use crate::consts::constant_simple; +use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; use crate::source::{snippet_opt, walk_span_to_context, SpanRange, SpanRangeExt}; use crate::tokenize_with_text; @@ -255,8 +255,8 @@ impl HirEqInterExpr<'_, '_, '_> { if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right) && let (Some(l), Some(r)) = ( - constant_simple(self.inner.cx, typeck_lhs, left), - constant_simple(self.inner.cx, typeck_rhs, right), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_lhs).eval_simple(left), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_rhs).eval_simple(right), ) && l == r { @@ -714,9 +714,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { #[expect(clippy::too_many_lines)] pub fn hash_expr(&mut self, e: &Expr<'_>) { - let simple_const = self - .maybe_typeck_results - .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e)); + let simple_const = self.maybe_typeck_results.and_then(|typeck_results| { + ConstEvalCtxt::with_env(self.cx.tcx, self.cx.param_env, typeck_results).eval_simple(e) + }); // const hashing may result in the same hash as some unrelated node, so add a sort of // discriminant depending on which path we're choosing next diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1d5f1a2a2bb13..af74e4b67c162 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -10,7 +10,6 @@ #![feature(assert_matches)] #![feature(unwrap_infallible)] #![recursion_limit = "512"] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow( clippy::missing_errors_doc, clippy::missing_panics_doc, @@ -126,7 +125,7 @@ use rustc_span::{sym, Span}; use rustc_target::abi::Integer; use visitors::Visitable; -use crate::consts::{constant, mir_to_const, Constant}; +use crate::consts::{mir_to_const, ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -211,20 +210,24 @@ pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool { false } -/// Returns `true` if the given `HirId` is inside a constant context. +/// Checks if we are currently in a const context (e.g. `const fn`, `static`/`const` initializer). /// -/// This is the same as `is_inside_always_const_context`, but also includes -/// `const fn`. +/// The current context is determined based on the current body which is set before calling a lint's +/// entry point (any function on `LateLintPass`). If you need to check in a different context use +/// `tcx.hir().is_inside_const_context(_)`. /// -/// # Example -/// -/// ```rust,ignore -/// if in_constant(cx, expr.hir_id) { -/// // Do something -/// } -/// ``` -pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { - cx.tcx.hir().is_inside_const_context(id) +/// Do not call this unless the `LateContext` has an enclosing body. For release build this case +/// will safely return `false`, but debug builds will ICE. Note that `check_expr`, `check_block`, +/// `check_pat` and a few other entry points will always have an enclosing body. Some entry points +/// like `check_path` or `check_ty` may or may not have one. +pub fn is_in_const_context(cx: &LateContext<'_>) -> bool { + debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body"); + cx.enclosing_body.is_some_and(|id| { + cx.tcx + .hir() + .body_const_context(cx.tcx.hir().body_owner_def_id(id)) + .is_some() + }) } /// Returns `true` if the given `HirId` is inside an always constant context. @@ -589,9 +592,8 @@ fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<It "u128" => SimplifiedType::Uint(UintTy::U128), "f32" => SimplifiedType::Float(FloatTy::F32), "f64" => SimplifiedType::Float(FloatTy::F64), - #[allow(trivial_casts)] _ => { - return Result::<_, rustc_errors::ErrorGuaranteed>::Ok(&[] as &[_]) + return Result::<&[_], rustc_errors::ErrorGuaranteed>::Ok(&[]) .into_iter() .flatten() .copied(); @@ -1579,8 +1581,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx) - && let Some(min_const) = mir_to_const(cx, Const::from_ty_const(min_val, bnd_ty, cx.tcx)) - && let Some(start_const) = constant(cx, cx.typeck_results(), start) + && let Some(min_const) = mir_to_const(cx.tcx, Const::from_ty_const(min_val, bnd_ty, cx.tcx)) + && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start) { start_const == min_const } else { @@ -1592,8 +1594,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx) - && let Some(max_const) = mir_to_const(cx, Const::from_ty_const(max_val, bnd_ty, cx.tcx)) - && let Some(end_const) = constant(cx, cx.typeck_results(), end) + && let Some(max_const) = mir_to_const(cx.tcx, Const::from_ty_const(max_val, bnd_ty, cx.tcx)) + && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end) { end_const == max_const } else { @@ -1624,7 +1626,9 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool return true; } let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id); - if let Some(Constant::Int(v)) = constant(cx, cx.tcx.typeck(enclosing_body), e) { + if let Some(Constant::Int(v)) = + ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(enclosing_body), cx.tcx.typeck(enclosing_body)).eval(e) + { return value == v; } false diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index d5a3d8b9e5a2e..684c645c19964 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -16,7 +16,6 @@ pub const BINARYHEAP_ITER: [&str; 5] = ["alloc", "collections", "binary_heap", " pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; 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 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"]; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 496c8f5b55373..96dd3c55d37ae 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -6,7 +6,8 @@ use rustc_ast::{LitKind, StrStyle}; use rustc_data_structures::sync::Lrc; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::{EarlyContext, LateContext}; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::source_map::{original_sp, SourceMap}; use rustc_span::{ @@ -17,6 +18,30 @@ use std::borrow::Cow; use std::fmt; use std::ops::Range; +pub trait HasSession { + fn sess(&self) -> &Session; +} +impl HasSession for Session { + fn sess(&self) -> &Session { + self + } +} +impl HasSession for TyCtxt<'_> { + fn sess(&self) -> &Session { + self.sess + } +} +impl HasSession for EarlyContext<'_> { + fn sess(&self) -> &Session { + ::rustc_lint::LintContext::sess(self) + } +} +impl HasSession for LateContext<'_> { + fn sess(&self) -> &Session { + self.tcx.sess() + } +} + /// Conversion of a value into the range portion of a `Span`. pub trait SpanRange: Sized { fn into_range(self) -> Range<BytePos>; @@ -71,19 +96,19 @@ impl IntoSpan for Range<BytePos> { pub trait SpanRangeExt: SpanRange { /// Gets the source file, and range in the file, of the given span. Returns `None` if the span /// extends through multiple files, or is malformed. - fn get_source_text(self, cx: &impl LintContext) -> Option<SourceFileRange> { + fn get_source_text(self, cx: &impl HasSession) -> Option<SourceFileRange> { get_source_text(cx.sess().source_map(), self.into_range()) } /// Calls the given function with the source text referenced and returns the value. Returns /// `None` if the source text cannot be retrieved. - fn with_source_text<T>(self, cx: &impl LintContext, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> { + fn with_source_text<T>(self, cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> { with_source_text(cx.sess().source_map(), self.into_range(), f) } /// Checks if the referenced source text satisfies the given predicate. Returns `false` if the /// source text cannot be retrieved. - fn check_source_text(self, cx: &impl LintContext, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { + fn check_source_text(self, cx: &impl HasSession, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { self.with_source_text(cx, pred).unwrap_or(false) } @@ -91,7 +116,7 @@ pub trait SpanRangeExt: SpanRange { /// and returns the value. Returns `None` if the source text cannot be retrieved. fn with_source_text_and_range<T>( self, - cx: &impl LintContext, + cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T, ) -> Option<T> { with_source_text_and_range(cx.sess().source_map(), self.into_range(), f) @@ -104,30 +129,30 @@ pub trait SpanRangeExt: SpanRange { /// The new range must reside within the same source file. fn map_range( self, - cx: &impl LintContext, + cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>, ) -> Option<Range<BytePos>> { map_range(cx.sess().source_map(), self.into_range(), f) } /// Extends the range to include all preceding whitespace characters. - fn with_leading_whitespace(self, cx: &impl LintContext) -> Range<BytePos> { + fn with_leading_whitespace(self, cx: &impl HasSession) -> Range<BytePos> { with_leading_whitespace(cx.sess().source_map(), self.into_range()) } /// Trims the leading whitespace from the range. - fn trim_start(self, cx: &impl LintContext) -> Range<BytePos> { + fn trim_start(self, cx: &impl HasSession) -> Range<BytePos> { trim_start(cx.sess().source_map(), self.into_range()) } /// Writes the referenced source text to the given writer. Will return `Err` if the source text /// could not be retrieved. - fn write_source_text_to(self, cx: &impl LintContext, dst: &mut impl fmt::Write) -> fmt::Result { + fn write_source_text_to(self, cx: &impl HasSession, dst: &mut impl fmt::Write) -> fmt::Result { write_source_text_to(cx.sess().source_map(), self.into_range(), dst) } /// Extracts the referenced source text as an owned string. - fn source_text_to_string(self, cx: &impl LintContext) -> Option<String> { + fn source_text_to_string(self, cx: &impl HasSession) -> Option<String> { self.with_source_text(cx, ToOwned::to_owned) } } @@ -227,15 +252,15 @@ impl SourceFileRange { } /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. -pub fn expr_block<T: LintContext>( - cx: &T, +pub fn expr_block( + sess: &impl HasSession, expr: &Expr<'_>, outer: SyntaxContext, default: &str, indent_relative_to: Option<Span>, app: &mut Applicability, ) -> String { - let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app); + let (code, from_macro) = snippet_block_with_context(sess, expr.span, outer, default, indent_relative_to, app); if !from_macro && let ExprKind::Block(block, _) = expr.kind && block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) @@ -260,13 +285,13 @@ pub fn expr_block<T: LintContext>( /// let x = (); /// // ^^^^^^^^^^ /// ``` -pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span { - first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) +pub fn first_line_of_span(sess: &impl HasSession, span: Span) -> Span { + first_char_in_first_line(sess, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) } -fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> { - let line_span = line_span(cx, span); - snippet_opt(cx, line_span).and_then(|snip| { +fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option<BytePos> { + let line_span = line_span(sess, span); + snippet_opt(sess, line_span).and_then(|snip| { snip.find(|c: char| !c.is_whitespace()) .map(|pos| line_span.lo() + BytePos::from_usize(pos)) }) @@ -281,9 +306,9 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo /// let x = (); /// // ^^^^^^^^^^^^^^ /// ``` -fn line_span<T: LintContext>(cx: &T, span: Span) -> Span { +fn line_span(sess: &impl HasSession, span: Span) -> Span { let span = original_sp(span, DUMMY_SP); - let SourceFileAndLine { sf, line } = cx.sess().source_map().lookup_line(span.lo()).unwrap(); + let SourceFileAndLine { sf, line } = sess.sess().source_map().lookup_line(span.lo()).unwrap(); let line_start = sf.lines()[line]; let line_start = sf.absolute_position(line_start); span.with_lo(line_start) @@ -297,13 +322,13 @@ fn line_span<T: LintContext>(cx: &T, span: Span) -> Span { /// let x = (); /// // ^^ -- will return 4 /// ``` -pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> { - snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) +pub fn indent_of(sess: &impl HasSession, span: Span) -> Option<usize> { + snippet_opt(sess, line_span(sess, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } /// Gets a snippet of the indentation of the line of a span -pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> { - snippet_opt(cx, line_span(cx, span)).map(|mut s| { +pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option<String> { + snippet_opt(sess, line_span(sess, span)).map(|mut s| { let len = s.len() - s.trim_start().len(); s.truncate(len); s @@ -315,8 +340,8 @@ pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> { // sources that the user has no control over. // For some reason these attributes don't have any expansion info on them, so // we have to check it this way until there is a better way. -pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool { - if let Some(snippet) = snippet_opt(cx, span) { +pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool { + if let Some(snippet) = snippet_opt(sess, span) { if snippet.is_empty() { return false; } @@ -407,8 +432,8 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, /// snippet(cx, span1, "..") // -> "value" /// snippet(cx, span2, "..") // -> "Vec::new()" /// ``` -pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { - snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from) +pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { + snippet_opt(sess, span).map_or_else(|| Cow::Borrowed(default), From::from) } /// Same as [`snippet`], but it adapts the applicability level by following rules: @@ -417,13 +442,13 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow< /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. /// - If the default value is used and the applicability level is `MachineApplicable`, change it to /// `HasPlaceholders` -pub fn snippet_with_applicability<'a, T: LintContext>( - cx: &T, +pub fn snippet_with_applicability<'a>( + sess: &impl HasSession, span: Span, default: &'a str, applicability: &mut Applicability, ) -> Cow<'a, str> { - snippet_with_applicability_sess(cx.sess(), span, default, applicability) + snippet_with_applicability_sess(sess.sess(), span, default, applicability) } fn snippet_with_applicability_sess<'a>( @@ -435,7 +460,7 @@ fn snippet_with_applicability_sess<'a>( if *applicability != Applicability::Unspecified && span.from_expansion() { *applicability = Applicability::MaybeIncorrect; } - snippet_opt_sess(sess, span).map_or_else( + snippet_opt(sess, span).map_or_else( || { if *applicability == Applicability::MachineApplicable { *applicability = Applicability::HasPlaceholders; @@ -447,12 +472,8 @@ fn snippet_with_applicability_sess<'a>( } /// Converts a span to a code snippet. Returns `None` if not available. -pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> { - snippet_opt_sess(cx.sess(), span) -} - -fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> { - sess.source_map().span_to_snippet(span).ok() +pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option<String> { + sess.sess().source_map().span_to_snippet(span).ok() } /// Converts a span (from a block) to a code snippet if available, otherwise use default. @@ -489,41 +510,41 @@ fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> { /// } // aligned with `if` /// ``` /// Note that the first line of the snippet always has 0 indentation. -pub fn snippet_block<'a, T: LintContext>( - cx: &T, +pub fn snippet_block<'a>( + sess: &impl HasSession, span: Span, default: &'a str, indent_relative_to: Option<Span>, ) -> Cow<'a, str> { - let snip = snippet(cx, span, default); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + let snip = snippet(sess, span, default); + let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); reindent_multiline(snip, true, indent) } /// Same as `snippet_block`, but adapts the applicability level by the rules of /// `snippet_with_applicability`. pub fn snippet_block_with_applicability<'a>( - cx: &impl LintContext, + sess: &impl HasSession, span: Span, default: &'a str, indent_relative_to: Option<Span>, applicability: &mut Applicability, ) -> Cow<'a, str> { - let snip = snippet_with_applicability(cx, span, default, applicability); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + let snip = snippet_with_applicability(sess, span, default, applicability); + let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); reindent_multiline(snip, true, indent) } pub fn snippet_block_with_context<'a>( - cx: &impl LintContext, + sess: &impl HasSession, span: Span, outer: SyntaxContext, default: &'a str, indent_relative_to: Option<Span>, app: &mut Applicability, ) -> (Cow<'a, str>, bool) { - let (snip, from_macro) = snippet_with_context(cx, span, outer, default, app); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app); + let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); (reindent_multiline(snip, true, indent), from_macro) } @@ -537,13 +558,13 @@ pub fn snippet_block_with_context<'a>( /// /// This will also return whether or not the snippet is a macro call. pub fn snippet_with_context<'a>( - cx: &impl LintContext, + sess: &impl HasSession, span: Span, outer: SyntaxContext, default: &'a str, applicability: &mut Applicability, ) -> (Cow<'a, str>, bool) { - snippet_with_context_sess(cx.sess(), span, outer, default, applicability) + snippet_with_context_sess(sess.sess(), span, outer, default, applicability) } fn snippet_with_context_sess<'a>( @@ -661,15 +682,15 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span { /// writeln!(o, "") -> writeln!(o, "") /// ^^ ^^^^ /// ``` -pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { - let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true); +pub fn expand_past_previous_comma(sess: &impl HasSession, span: Span) -> Span { + let extended = sess.sess().source_map().span_extend_to_prev_char(span, ',', true); extended.with_lo(extended.lo() - BytePos(1)) } /// Converts `expr` to a `char` literal if it's a `str` literal containing a single /// character (or a single byte with `ascii_only`) pub fn str_literal_to_char_literal( - cx: &LateContext<'_>, + sess: &impl HasSession, expr: &Expr<'_>, applicability: &mut Applicability, ascii_only: bool, @@ -684,7 +705,7 @@ pub fn str_literal_to_char_literal( } && len == 1 { - let snip = snippet_with_applicability(cx, expr.span, string, applicability); + let snip = snippet_with_applicability(sess, expr.span, string, applicability); let ch = if let StrStyle::Raw(nhash) = style { let nhash = nhash as usize; // for raw string: r##"a"## diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 812fb647fdab6..bd48990aea95f 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -525,19 +525,6 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) } -/// Peels off all references on the type. Returns the underlying type and the number of references -/// removed. -pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { - fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { - if let ty::Ref(_, ty, _) = ty.kind() { - peel(*ty, count + 1) - } else { - (ty, count) - } - } - peel(ty, 0) -} - /// Peels off all references on the type. Returns the underlying type, the number of references /// removed, and whether the pointer is ultimately mutable or not. pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 7066c9ad2b96b..2a5d3536ff6b7 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,6 +1,7 @@ use crate::ty::needs_ordered_drop; use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; +use rustc_ast::visit::{try_visit, VisitorResult}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor}; @@ -50,16 +51,17 @@ impl Continue for Descend { /// A type which can be visited. pub trait Visitable<'tcx> { /// Calls the corresponding `visit_*` function on the visitor. - fn visit<V: Visitor<'tcx>>(self, visitor: &mut V); + fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result; } impl<'tcx, T> Visitable<'tcx> for &'tcx [T] where &'tcx T: Visitable<'tcx>, { - fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) { + fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result { for x in self { - x.visit(visitor); + try_visit!(x.visit(visitor)); } + V::Result::output() } } impl<'tcx, A, B> Visitable<'tcx> for (A, B) @@ -67,27 +69,28 @@ where A: Visitable<'tcx>, B: Visitable<'tcx>, { - fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) { + fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result { let (a, b) = self; - a.visit(visitor); - b.visit(visitor); + try_visit!(a.visit(visitor)); + b.visit(visitor) } } impl<'tcx, T> Visitable<'tcx> for Option<T> where T: Visitable<'tcx>, { - fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) { + fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result { if let Some(x) = self { - x.visit(visitor); + try_visit!(x.visit(visitor)); } + V::Result::output() } } macro_rules! visitable_ref { ($t:ident, $f:ident) => { impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { - fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) { - visitor.$f(self); + fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result { + visitor.$f(self) } } }; @@ -104,45 +107,37 @@ pub fn for_each_expr_without_closures<'tcx, B, C: Continue>( node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>, ) -> Option<B> { - struct V<B, F> { + struct V<F> { f: F, - res: Option<B>, } - impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<B, F> { - type Result = ControlFlow<()>; + impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<F> { + type Result = ControlFlow<B>; - fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> ControlFlow<()> { - if self.res.is_some() { - return ControlFlow::Break(()); - } + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result { match (self.f)(e) { ControlFlow::Continue(c) if c.descend() => walk_expr(self, e), - ControlFlow::Break(b) => { - self.res = Some(b); - ControlFlow::Break(()) - }, + ControlFlow::Break(b) => ControlFlow::Break(b), ControlFlow::Continue(_) => ControlFlow::Continue(()), } } // Avoid unnecessary `walk_*` calls. - fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> ControlFlow<()> { + fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> Self::Result { ControlFlow::Continue(()) } - fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> ControlFlow<()> { + fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result { ControlFlow::Continue(()) } - fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> ControlFlow<()> { + fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> Self::Result { ControlFlow::Continue(()) } // Avoid monomorphising all `visit_*` functions. - fn visit_nested_item(&mut self, _: ItemId) -> ControlFlow<()> { + fn visit_nested_item(&mut self, _: ItemId) -> Self::Result { ControlFlow::Continue(()) } } - let mut v = V { f, res: None }; - node.visit(&mut v); - v.res + let mut v = V { f }; + node.visit(&mut v).break_value() } /// Calls the given function once for each expression contained. This will enter bodies, but not @@ -152,44 +147,47 @@ pub fn for_each_expr<'tcx, B, C: Continue>( node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>, ) -> Option<B> { - struct V<'tcx, B, F> { + struct V<'tcx, F> { tcx: TyCtxt<'tcx>, f: F, - res: Option<B>, } - impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<'tcx, B, F> { + impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<'tcx, F> { type NestedFilter = nested_filter::OnlyBodies; + type Result = ControlFlow<B>; + fn nested_visit_map(&mut self) -> Self::Map { self.tcx.hir() } - fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { - if self.res.is_some() { - return; - } + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result { match (self.f)(e) { ControlFlow::Continue(c) if c.descend() => walk_expr(self, e), - ControlFlow::Break(b) => self.res = Some(b), - ControlFlow::Continue(_) => (), + ControlFlow::Break(b) => ControlFlow::Break(b), + ControlFlow::Continue(_) => ControlFlow::Continue(()), } } // Only walk closures - fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} + fn visit_anon_const(&mut self, _: &'tcx AnonConst) -> Self::Result { + ControlFlow::Continue(()) + } // Avoid unnecessary `walk_*` calls. - fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {} - fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {} - fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {} + fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> Self::Result { + ControlFlow::Continue(()) + } + fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result { + ControlFlow::Continue(()) + } + fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> Self::Result { + ControlFlow::Continue(()) + } // Avoid monomorphising all `visit_*` functions. - fn visit_nested_item(&mut self, _: ItemId) {} + fn visit_nested_item(&mut self, _: ItemId) -> Self::Result { + ControlFlow::Continue(()) + } } - let mut v = V { - tcx: cx.tcx, - f, - res: None, - }; - node.visit(&mut v); - v.res + let mut v = V { tcx: cx.tcx, f }; + node.visit(&mut v).break_value() } /// returns `true` if expr contains match expr desugared from try diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 80106f683c2ae..31270241b0b2e 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -11,6 +11,3 @@ proc-macro = true itertools = "0.12" quote = "1.0.21" syn = "2.0" - -[features] -deny-warnings = [] diff --git a/declare_clippy_lint/src/lib.rs b/declare_clippy_lint/src/lib.rs index 25b2fc9395cd9..ca070f6c250bf 100644 --- a/declare_clippy_lint/src/lib.rs +++ b/declare_clippy_lint/src/lib.rs @@ -1,5 +1,4 @@ #![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)] diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index 350418eeeb871..b0e4e3e3e573b 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -25,6 +25,3 @@ tar = "0.4" toml = "0.7.3" ureq = { version = "2.2", features = ["json"] } walkdir = "2.3" - -[features] -deny-warnings = [] diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index 6bec1753fc7b8..bd4fcc5e337e9 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -33,13 +33,13 @@ pub(crate) struct LintcheckConfig { /// Runs cargo clippy --fix and checks if all suggestions apply #[clap(long, conflicts_with("max_jobs"))] pub fix: bool, - /// Apply a filter to only collect specified lints, this also overrides `allow` attributes + /// Apply a filter to only collect specified lints #[clap(long = "filter", value_name = "clippy_lint_name", use_value_delimiter = true)] pub lint_filter: Vec<String>, - /// Set all lints to the "warn" lint level, even resitriction ones. Usually, - /// it's better to use `--filter` instead + /// Check all Clippy lints, by default only `clippy::all` and `clippy::pedantic` are checked. + /// Usually, it's better to use `--filter` instead #[clap(long, conflicts_with("lint_filter"))] - pub warn_all: bool, + pub all_lints: bool, /// Set the output format of the log file #[clap(long, short, default_value = "text")] pub format: OutputFormat, diff --git a/lintcheck/src/input.rs b/lintcheck/src/input.rs index 3b263674aa87a..3383d50fa02c7 100644 --- a/lintcheck/src/input.rs +++ b/lintcheck/src/input.rs @@ -98,12 +98,12 @@ pub fn read_crates(toml_path: &Path) -> (Vec<CrateWithSource>, RecursiveOptions) let crate_list: SourceList = 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<TomlCrate> = crate_list.crates.into_values().collect(); + let toml_crates: Vec<TomlCrate> = crate_list.crates.into_values().collect(); // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => - // multiple Cratesources) + // multiple CrateSources) let mut crate_sources = Vec::new(); - for tk in tomlcrates { + for tk in toml_crates { if let Some(ref path) = tk.path { crate_sources.push(CrateWithSource { name: tk.name.clone(), diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 0dd62ded293c8..acb6eaa5278aa 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -119,7 +119,8 @@ impl Crate { cmd.arg(if config.fix { "fix" } else { "check" }) .arg("--quiet") .current_dir(&self.path) - .env("CLIPPY_ARGS", clippy_args.join("__CLIPPY_HACKERY__")); + .env("CLIPPY_ARGS", clippy_args.join("__CLIPPY_HACKERY__")) + .env("CLIPPY_DISABLE_DOCS_LINKS", "1"); if let Some(server) = server { // `cargo clippy` is a wrapper around `cargo check` that mainly sets `RUSTC_WORKSPACE_WRAPPER` to @@ -284,29 +285,24 @@ fn lintcheck(config: LintcheckConfig) { let (crates, recursive_options) = read_crates(&config.sources_toml_path); let counter = AtomicUsize::new(1); - let mut lint_level_args: Vec<String> = vec![]; + let mut lint_level_args: Vec<String> = vec!["--cap-lints=allow".into()]; if config.lint_filter.is_empty() { - lint_level_args.push("--cap-lints=warn".to_string()); - - // Set allow-by-default to warn - if config.warn_all { - [ + let groups = if config.all_lints { + &[ + "clippy::all", "clippy::cargo", "clippy::nursery", "clippy::pedantic", "clippy::restriction", - ] + ][..] + } else { + &["clippy::all", "clippy::pedantic"] + }; + groups .iter() - .map(|group| format!("--warn={group}")) + .map(|group| format!("--force-warn={group}")) .collect_into(&mut lint_level_args); - } else { - ["clippy::cargo", "clippy::pedantic"] - .iter() - .map(|group| format!("--warn={group}")) - .collect_into(&mut lint_level_args); - } } else { - lint_level_args.push("--cap-lints=allow".to_string()); config .lint_filter .iter() diff --git a/rust-toolchain b/rust-toolchain index 69fb11a4824af..5fbe4e544a9e4 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-07-25" +channel = "nightly-2024-08-08" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/rustc_tools_util/Cargo.toml b/rustc_tools_util/Cargo.toml index 877049ae7d0eb..37b592da132f4 100644 --- a/rustc_tools_util/Cargo.toml +++ b/rustc_tools_util/Cargo.toml @@ -10,6 +10,3 @@ categories = ["development-tools"] edition = "2018" [dependencies] - -[features] -deny-warnings = [] diff --git a/rustc_tools_util/src/lib.rs b/rustc_tools_util/src/lib.rs index 4c1d8c3733df6..2cc38130472f8 100644 --- a/rustc_tools_util/src/lib.rs +++ b/rustc_tools_util/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] +use std::str; /// This macro creates the version string during compilation from the /// current environment @@ -101,49 +101,46 @@ impl std::fmt::Debug for VersionInfo { #[must_use] pub fn get_commit_hash() -> Option<String> { - std::process::Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) + let output = std::process::Command::new("git") + .args(["rev-parse", "HEAD"]) .output() - .ok() - .and_then(|r| String::from_utf8(r.stdout).ok()) + .ok()?; + let mut stdout = output.status.success().then_some(output.stdout)?; + stdout.truncate(10); + String::from_utf8(stdout).ok() } #[must_use] pub fn get_commit_date() -> Option<String> { - std::process::Command::new("git") + let output = std::process::Command::new("git") .args(["log", "-1", "--date=short", "--pretty=format:%cd"]) .output() - .ok() - .and_then(|r| String::from_utf8(r.stdout).ok()) + .ok()?; + let stdout = output.status.success().then_some(output.stdout)?; + String::from_utf8(stdout).ok() } #[must_use] pub fn get_channel() -> String { - match std::env::var("CFG_RELEASE_CHANNEL") { - Ok(channel) => channel, - Err(_) => { - // if that failed, try to ask rustc -V, do some parsing and find out - match std::process::Command::new("rustc") - .arg("-V") - .output() - .ok() - .and_then(|r| String::from_utf8(r.stdout).ok()) - { - Some(rustc_output) => { - if rustc_output.contains("beta") { - String::from("beta") - } else if rustc_output.contains("stable") { - String::from("stable") - } else { - // default to nightly if we fail to parse - String::from("nightly") - } - }, - // default to nightly - None => String::from("nightly"), + if let Ok(channel) = std::env::var("CFG_RELEASE_CHANNEL") { + return channel; + } + + // if that failed, try to ask rustc -V, do some parsing and find out + if let Ok(output) = std::process::Command::new("rustc").arg("-V").output() { + if output.status.success() { + if let Ok(rustc_output) = str::from_utf8(&output.stdout) { + if rustc_output.contains("beta") { + return String::from("beta"); + } else if rustc_output.contains("stable") { + return String::from("stable"); + } } - }, + } } + + // default to nightly + String::from("nightly") } #[cfg(test)] diff --git a/src/driver.rs b/src/driver.rs index 3fafe2427a256..0ac3f35b4465a 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -2,7 +2,6 @@ #![allow(rustc::untranslatable_diagnostic)] #![feature(rustc_private)] #![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)] // warn on rustc internal lints @@ -151,7 +150,6 @@ impl rustc_driver::Callbacks for ClippyCallbacks { let conf = clippy_config::Conf::read(sess, &conf_path); clippy_lints::register_lints(lint_store, conf); clippy_lints::register_pre_expansion_lints(lint_store, conf); - clippy_lints::register_renamed(lint_store); })); // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be diff --git a/src/main.rs b/src/main.rs index 30beaae34d2fe..c9af2138a7233 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -#![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)] diff --git a/tests/check-fmt.rs b/tests/check-fmt.rs index e106583de4a2e..cd3fed896c2de 100644 --- a/tests/check-fmt.rs +++ b/tests/check-fmt.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] use std::path::PathBuf; diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 4e5120406b0d8..c7080e5dcdc9e 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] @@ -208,7 +207,8 @@ fn run_ui_toml() { ui_test::run_tests_generic( vec![config], ui_test::default_file_filter, - |config, path, _file_contents| { + |config, file_contents| { + let path = file_contents.span().file; config .program .envs @@ -260,7 +260,7 @@ fn run_ui_cargo() { path.ends_with("Cargo.toml") .then(|| ui_test::default_any_file_filter(path, config) && !ignored_32bit(path)) }, - |_config, _path, _file_contents| {}, + |_config, _file_contents| {}, status_emitter::Text::from(args.format), ) .unwrap(); diff --git a/tests/dogfood.rs b/tests/dogfood.rs index c8a761bf509ec..8d10d5e7161a6 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -3,7 +3,6 @@ //! //! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] use itertools::Itertools; diff --git a/tests/integration.rs b/tests/integration.rs index 77b7bb6a7bf69..13cf36823c5e4 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -8,7 +8,6 @@ //! Clippy doesn't produce an ICE. Lint warnings are ignored by this test. #![cfg(feature = "integration")] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] use std::env; diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 6ce7e44474d81..7ed1f485c1cfd 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] use std::ffi::OsStr; diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 141c11ddb47f9..a8225d037e828 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::assertions_on_constants)] #![feature(path_file_prefix)] diff --git a/tests/ui-internal/default_deprecation_reason.rs b/tests/ui-internal/default_deprecation_reason.rs deleted file mode 100644 index c8961d5e1f0bd..0000000000000 --- a/tests/ui-internal/default_deprecation_reason.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![deny(clippy::internal)] -#![feature(rustc_private)] - -#[macro_use] -extern crate clippy_lints; -use clippy_lints::deprecated_lints::ClippyDeprecatedLint; - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// TODO - #[clippy::version = "1.63.0"] - pub COOL_LINT_DEFAULT, - "default deprecation note" -} - -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`" -} - -fn main() {} diff --git a/tests/ui-internal/default_deprecation_reason.stderr b/tests/ui-internal/default_deprecation_reason.stderr deleted file mode 100644 index 3b7c747c23f41..0000000000000 --- a/tests/ui-internal/default_deprecation_reason.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: the lint `COOL_LINT_DEFAULT` has the default deprecation reason - --> tests/ui-internal/default_deprecation_reason.rs:8:1 - | -LL | / declare_deprecated_lint! { -LL | | /// ### What it does -LL | | /// Nothing. This lint has been deprecated. -LL | | /// -... | -LL | | "default deprecation note" -LL | | } - | |_^ - | -note: the lint level is defined here - --> tests/ui-internal/default_deprecation_reason.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::default_deprecation_reason)]` implied by `#[deny(clippy::internal)]` - = note: this error originates in the macro `declare_deprecated_lint` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - diff --git a/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/tests/ui-toml/excessive_nesting/excessive_nesting.rs index 4375f324acaa6..858aab528a91a 100644 --- a/tests/ui-toml/excessive_nesting/excessive_nesting.rs +++ b/tests/ui-toml/excessive_nesting/excessive_nesting.rs @@ -1,15 +1,19 @@ //@aux-build:../../ui/auxiliary/proc_macros.rs #![rustfmt::skip] #![feature(custom_inner_attributes)] -#![allow(unused)] -#![allow(clippy::let_and_return)] -#![allow(clippy::redundant_closure_call)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::never_loop)] -#![allow(clippy::needless_if)] #![warn(clippy::excessive_nesting)] -#![allow(clippy::collapsible_if, clippy::blocks_in_conditions)] +#![allow( + unused, + clippy::let_and_return, + clippy::redundant_closure_call, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::never_loop, + clippy::needless_if, + clippy::collapsible_if, + clippy::blocks_in_conditions, + clippy::single_match, +)] #[macro_use] extern crate proc_macros; diff --git a/tests/ui-toml/excessive_nesting/excessive_nesting.stderr b/tests/ui-toml/excessive_nesting/excessive_nesting.stderr index dafcd4420554b..ccdaecdd48170 100644 --- a/tests/ui-toml/excessive_nesting/excessive_nesting.stderr +++ b/tests/ui-toml/excessive_nesting/excessive_nesting.stderr @@ -1,5 +1,5 @@ error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:21:25 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:25:25 | LL | let w = { 3 }; | ^^^^^ @@ -9,7 +9,7 @@ LL | let w = { 3 }; = help: to override `-D warnings` add `#[allow(clippy::excessive_nesting)]` error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:67:17 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:71:17 | LL | / impl C { LL | | pub fn c() {} @@ -19,7 +19,7 @@ LL | | } = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:81:25 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:85:25 | LL | let x = { 1 }; // not a warning, but cc is | ^^^^^ @@ -27,7 +27,7 @@ LL | let x = { 1 }; // not a warning, but cc is = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:98:17 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:102:17 | LL | / pub mod e { LL | | pub mod f {} @@ -37,7 +37,7 @@ LL | | } // not here = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:111:18 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:115:18 | LL | a_but_not({{{{{{{{0}}}}}}}}); | ^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | a_but_not({{{{{{{{0}}}}}}}}); = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:112:12 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:116:12 | LL | a.a({{{{{{{{{0}}}}}}}}}); | ^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL | a.a({{{{{{{{{0}}}}}}}}}); = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:113:12 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:117:12 | LL | (0, {{{{{{{1}}}}}}}); | ^^^^^^^^^ @@ -61,7 +61,7 @@ LL | (0, {{{{{{{1}}}}}}}); = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:118:25 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:122:25 | LL | if true { | _________________________^ @@ -74,7 +74,7 @@ LL | | } = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:130:29 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:134:29 | LL | let z = (|| { | _____________________________^ @@ -86,7 +86,7 @@ LL | | })(); = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:149:13 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:153:13 | LL | y += {{{{{5}}}}}; | ^^^^^ @@ -94,7 +94,7 @@ LL | y += {{{{{5}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:150:20 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:154:20 | LL | let z = y + {{{{{{{{{5}}}}}}}}}; | ^^^^^^^^^^^^^ @@ -102,7 +102,7 @@ LL | let z = y + {{{{{{{{{5}}}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:151:12 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:155:12 | LL | [0, {{{{{{{{{{0}}}}}}}}}}]; | ^^^^^^^^^^^^^^^ @@ -110,7 +110,7 @@ LL | [0, {{{{{{{{{{0}}}}}}}}}}]; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:152:25 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:156:25 | LL | let mut xx = [0; {{{{{{{{100}}}}}}}}]; | ^^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | let mut xx = [0; {{{{{{{{100}}}}}}}}]; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:153:11 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:157:11 | LL | xx[{{{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}}}]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -126,7 +126,7 @@ LL | xx[{{{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}}}]; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:154:13 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:158:13 | LL | &mut {{{{{{{{{{y}}}}}}}}}}; | ^^^^^^^^^^^^^^^ @@ -134,7 +134,7 @@ LL | &mut {{{{{{{{{{y}}}}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:156:17 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:17 | LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}} | ^^^^ @@ -142,7 +142,7 @@ LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:156:28 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:28 | LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}} | ^^^^^^^^^^ @@ -150,7 +150,7 @@ LL | for i in {{{{xx}}}} {{{{{{{{}}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:158:28 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:162:28 | LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} | ^^^^^^^^^^^^^ @@ -158,7 +158,7 @@ LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:158:48 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:162:48 | LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} | ^^^^^^^^ @@ -166,7 +166,7 @@ LL | while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:14 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:14 | LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} | ^^^^^^^^^^^^^^ @@ -174,7 +174,7 @@ LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:35 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:35 | LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} | ^^^^^^^^^^^^ @@ -182,7 +182,7 @@ LL | while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:162:23 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:166:23 | LL | let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -190,7 +190,7 @@ LL | let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} }; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:8 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:168:8 | LL | {{{{1;}}}}..{{{{{{3}}}}}}; | ^^^^ @@ -198,7 +198,7 @@ LL | {{{{1;}}}}..{{{{{{3}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:20 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:168:20 | LL | {{{{1;}}}}..{{{{{{3}}}}}}; | ^^^^^^^ @@ -206,7 +206,7 @@ LL | {{{{1;}}}}..{{{{{{3}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:165:8 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:169:8 | LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}}; | ^^^^ @@ -214,7 +214,7 @@ LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:165:21 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:169:21 | LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -222,7 +222,7 @@ LL | {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:166:10 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:170:10 | LL | ..{{{{{{{5}}}}}}}; | ^^^^^^^^^ @@ -230,7 +230,7 @@ LL | ..{{{{{{{5}}}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:167:11 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:171:11 | LL | ..={{{{{3}}}}}; | ^^^^^ @@ -238,7 +238,7 @@ LL | ..={{{{{3}}}}}; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:168:8 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:172:8 | LL | {{{{{1;}}}}}..; | ^^^^^^ @@ -246,7 +246,7 @@ LL | {{{{{1;}}}}}..; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:170:20 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:174:20 | LL | loop { break {{{{1}}}} }; | ^^^^^ @@ -254,7 +254,7 @@ LL | loop { break {{{{1}}}} }; = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:171:13 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:175:13 | LL | loop {{{{{{}}}}}} | ^^^^^^ @@ -262,7 +262,7 @@ LL | loop {{{{{{}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:173:14 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:177:14 | LL | match {{{{{{true}}}}}} { | ^^^^^^^^^^ @@ -270,7 +270,7 @@ LL | match {{{{{{true}}}}}} { = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:174:20 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:178:20 | LL | true => {{{{}}}}, | ^^ @@ -278,7 +278,7 @@ LL | true => {{{{}}}}, = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:175:21 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:179:21 | LL | false => {{{{}}}}, | ^^ @@ -286,7 +286,7 @@ LL | false => {{{{}}}}, = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:181:17 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:185:17 | LL | / { LL | | println!("warning! :)"); @@ -296,7 +296,7 @@ LL | | } = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:190:28 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:194:28 | LL | async fn c() -> u32 {{{{{{{0}}}}}}} | ^^^^^^^^^ @@ -304,7 +304,7 @@ LL | async fn c() -> u32 {{{{{{{0}}}}}}} = help: try refactoring your code to minimize nesting error: this block is too nested - --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:196:8 + --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:200:8 | LL | {{{{b().await}}}}; | ^^^^^^^^^^^ diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr index 41d5afd3efeca..320578bfabce0 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -1,11 +1,15 @@ -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:38:17 | LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::get-unwrap` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::get_unwrap)]` +help: using `[]` is clearer and more concise + | +LL | let _ = &boxed_slice[1]; + | ~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:38:17 @@ -18,11 +22,16 @@ LL | let _ = boxed_slice.get(1).unwrap(); = note: `-D clippy::unwrap-used` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unwrap_used)]` -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:39:17 | LL | let _ = some_slice.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_slice[0]; + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:39:17 @@ -33,11 +42,16 @@ LL | let _ = some_slice.get(0).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a Vec --> tests/ui-toml/unwrap_used/unwrap_used.rs:40:17 | LL | let _ = some_vec.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vec[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_vec[0]; + | ~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:40:17 @@ -48,11 +62,16 @@ LL | let _ = some_vec.get(0).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a VecDeque --> tests/ui-toml/unwrap_used/unwrap_used.rs:41:17 | LL | let _ = some_vecdeque.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vecdeque[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_vecdeque[0]; + | ~~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:41:17 @@ -63,11 +82,16 @@ LL | let _ = some_vecdeque.get(0).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a HashMap --> tests/ui-toml/unwrap_used/unwrap_used.rs:42:17 | LL | let _ = some_hashmap.get(&1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_hashmap[&1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_hashmap[&1]; + | ~~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:42:17 @@ -78,11 +102,16 @@ LL | let _ = some_hashmap.get(&1).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a BTreeMap --> tests/ui-toml/unwrap_used/unwrap_used.rs:43:17 | LL | let _ = some_btreemap.get(&1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_btreemap[&1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_btreemap[&1]; + | ~~~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:43:17 @@ -93,11 +122,16 @@ LL | let _ = some_btreemap.get(&1).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:47:21 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _: u8 = boxed_slice[1]; + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:47:22 @@ -108,11 +142,16 @@ LL | let _: u8 = *boxed_slice.get(1).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:52:9 | LL | *boxed_slice.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | boxed_slice[0] = 1; + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:52:10 @@ -123,11 +162,16 @@ LL | *boxed_slice.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:53:9 | LL | *some_slice.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | some_slice[0] = 1; + | ~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:53:10 @@ -138,11 +182,16 @@ LL | *some_slice.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a Vec --> tests/ui-toml/unwrap_used/unwrap_used.rs:54:9 | LL | *some_vec.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | some_vec[0] = 1; + | ~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:54:10 @@ -153,11 +202,16 @@ LL | *some_vec.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a VecDeque --> tests/ui-toml/unwrap_used/unwrap_used.rs:55:9 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vecdeque[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | some_vecdeque[0] = 1; + | ~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:55:10 @@ -168,11 +222,16 @@ LL | *some_vecdeque.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a Vec --> tests/ui-toml/unwrap_used/unwrap_used.rs:67:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = some_vec[0..1].to_vec(); + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:67:17 @@ -183,11 +242,16 @@ LL | let _ = some_vec.get(0..1).unwrap().to_vec(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a Vec --> tests/ui-toml/unwrap_used/unwrap_used.rs:68:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = some_vec[0..1].to_vec(); + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui-toml/unwrap_used/unwrap_used.rs:68:17 @@ -198,17 +262,27 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:75:13 | LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &boxed_slice[1]; + | ~~~~~~~~~~~~~~~ -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui-toml/unwrap_used/unwrap_used.rs:93:17 | LL | let _ = Box::new([0]).get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&Box::new([0])[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &Box::new([0])[1]; + | ~~~~~~~~~~~~~~~~~ error: aborting due to 28 previous errors diff --git a/tests/ui/assigning_clones.fixed b/tests/ui/assigning_clones.fixed index 60f6a385fc506..b376d55a40250 100644 --- a/tests/ui/assigning_clones.fixed +++ b/tests/ui/assigning_clones.fixed @@ -3,6 +3,7 @@ #![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612 #![allow(clippy::needless_late_init)] #![allow(clippy::box_collection)] +#![allow(clippy::boxed_local)] #![warn(clippy::assigning_clones)] use std::borrow::ToOwned; @@ -182,6 +183,31 @@ impl Clone for AvoidRecursiveCloneFrom { } } +// Deref handling +fn clone_into_deref_method(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) { + (*a).clone_from(&b); +} + +fn clone_into_deref_with_clone_method(mut a: DerefWrapperWithClone<HasCloneFrom>, b: HasCloneFrom) { + (*a).clone_from(&b); +} + +fn clone_into_box_method(mut a: Box<HasCloneFrom>, b: HasCloneFrom) { + (*a).clone_from(&b); +} + +fn clone_into_self_deref_method(a: &mut DerefWrapperWithClone<HasCloneFrom>, b: DerefWrapperWithClone<HasCloneFrom>) { + a.clone_from(&b); +} + +fn clone_into_deref_function(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) { + Clone::clone_from(&mut *a, &b); +} + +fn clone_into_box_function(mut a: Box<HasCloneFrom>, b: HasCloneFrom) { + Clone::clone_from(&mut *a, &b); +} + // ToOwned fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) { ref_str.clone_into(mut_string); @@ -328,3 +354,45 @@ mod borrowck_conflicts { path = path.components().as_path().to_owned(); } } + +struct DerefWrapper<T>(T); + +impl<T> Deref for DerefWrapper<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T> DerefMut for DerefWrapper<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +struct DerefWrapperWithClone<T>(T); + +impl<T> Deref for DerefWrapperWithClone<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T> DerefMut for DerefWrapperWithClone<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<T: Clone> Clone for DerefWrapperWithClone<T> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + + fn clone_from(&mut self, source: &Self) { + *self = Self(source.0.clone()); + } +} diff --git a/tests/ui/assigning_clones.rs b/tests/ui/assigning_clones.rs index 6eb85be511a89..11a5d4459c350 100644 --- a/tests/ui/assigning_clones.rs +++ b/tests/ui/assigning_clones.rs @@ -3,6 +3,7 @@ #![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612 #![allow(clippy::needless_late_init)] #![allow(clippy::box_collection)] +#![allow(clippy::boxed_local)] #![warn(clippy::assigning_clones)] use std::borrow::ToOwned; @@ -182,6 +183,31 @@ impl Clone for AvoidRecursiveCloneFrom { } } +// Deref handling +fn clone_into_deref_method(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) { + *a = b.clone(); +} + +fn clone_into_deref_with_clone_method(mut a: DerefWrapperWithClone<HasCloneFrom>, b: HasCloneFrom) { + *a = b.clone(); +} + +fn clone_into_box_method(mut a: Box<HasCloneFrom>, b: HasCloneFrom) { + *a = b.clone(); +} + +fn clone_into_self_deref_method(a: &mut DerefWrapperWithClone<HasCloneFrom>, b: DerefWrapperWithClone<HasCloneFrom>) { + *a = b.clone(); +} + +fn clone_into_deref_function(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) { + *a = Clone::clone(&b); +} + +fn clone_into_box_function(mut a: Box<HasCloneFrom>, b: HasCloneFrom) { + *a = Clone::clone(&b); +} + // ToOwned fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) { *mut_string = ref_str.to_owned(); @@ -328,3 +354,45 @@ mod borrowck_conflicts { path = path.components().as_path().to_owned(); } } + +struct DerefWrapper<T>(T); + +impl<T> Deref for DerefWrapper<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T> DerefMut for DerefWrapper<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +struct DerefWrapperWithClone<T>(T); + +impl<T> Deref for DerefWrapperWithClone<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T> DerefMut for DerefWrapperWithClone<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<T: Clone> Clone for DerefWrapperWithClone<T> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + + fn clone_from(&mut self, source: &Self) { + *self = Self(source.0.clone()); + } +} diff --git a/tests/ui/assigning_clones.stderr b/tests/ui/assigning_clones.stderr index a68516376abb5..19724a6d4ec2f 100644 --- a/tests/ui/assigning_clones.stderr +++ b/tests/ui/assigning_clones.stderr @@ -1,5 +1,5 @@ error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:24:5 + --> tests/ui/assigning_clones.rs:25:5 | LL | *mut_thing = value_thing.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(&value_thing)` @@ -8,142 +8,178 @@ LL | *mut_thing = value_thing.clone(); = help: to override `-D warnings` add `#[allow(clippy::assigning_clones)]` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:28:5 + --> tests/ui/assigning_clones.rs:29:5 | LL | *mut_thing = ref_thing.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:32:5 + --> tests/ui/assigning_clones.rs:33:5 | LL | mut_thing = ref_thing.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:36:5 + --> tests/ui/assigning_clones.rs:37:5 | LL | *mut_thing = Clone::clone(ref_thing); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:40:5 + --> tests/ui/assigning_clones.rs:41:5 | LL | mut_thing = Clone::clone(ref_thing); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut mut_thing, ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:44:5 + --> tests/ui/assigning_clones.rs:45:5 | LL | *mut_thing = Clone::clone(ref_thing); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:48:5 + --> tests/ui/assigning_clones.rs:49:5 | LL | *mut_thing = HasCloneFrom::clone(ref_thing); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:52:5 + --> tests/ui/assigning_clones.rs:53:5 | LL | *mut_thing = <HasCloneFrom as Clone>::clone(ref_thing); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:57:5 + --> tests/ui/assigning_clones.rs:58:5 | LL | *(mut_thing + &mut HasCloneFrom) = ref_thing.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `(mut_thing + &mut HasCloneFrom).clone_from(ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:62:5 + --> tests/ui/assigning_clones.rs:63:5 | LL | *mut_thing = (ref_thing + ref_thing).clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing + ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:67:5 + --> tests/ui/assigning_clones.rs:68:5 | LL | s = format!("{} {}", "hello", "world").clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `s.clone_from(&format!("{} {}", "hello", "world"))` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:72:5 + --> tests/ui/assigning_clones.rs:73:5 | LL | s = Clone::clone(&format!("{} {}", "hello", "world")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut s, &format!("{} {}", "hello", "world"))` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:78:9 + --> tests/ui/assigning_clones.rs:79:9 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:149:5 + --> tests/ui/assigning_clones.rs:150:5 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:156:5 + --> tests/ui/assigning_clones.rs:157:5 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:157:5 + --> tests/ui/assigning_clones.rs:158:5 | LL | a = c.to_owned(); | ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)` +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:188:5 + | +LL | *a = b.clone(); + | ^^^^^^^^^^^^^^ help: use `clone_from()`: `(*a).clone_from(&b)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:192:5 + | +LL | *a = b.clone(); + | ^^^^^^^^^^^^^^ help: use `clone_from()`: `(*a).clone_from(&b)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:196:5 + | +LL | *a = b.clone(); + | ^^^^^^^^^^^^^^ help: use `clone_from()`: `(*a).clone_from(&b)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:200:5 + | +LL | *a = b.clone(); + | ^^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:204:5 + | +LL | *a = Clone::clone(&b); + | ^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut *a, &b)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:208:5 + | +LL | *a = Clone::clone(&b); + | ^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut *a, &b)` + error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:187:5 + --> tests/ui/assigning_clones.rs:213:5 | LL | *mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:191:5 + --> tests/ui/assigning_clones.rs:217:5 | LL | mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:212:5 + --> tests/ui/assigning_clones.rs:238:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:216:5 + --> tests/ui/assigning_clones.rs:242:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:220:5 + --> tests/ui/assigning_clones.rs:246:5 | LL | *mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:224:5 + --> tests/ui/assigning_clones.rs:250:5 | LL | mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:229:5 + --> tests/ui/assigning_clones.rs:255:5 | LL | s = format!("{} {}", "hello", "world").to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `format!("{} {}", "hello", "world").clone_into(&mut s)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:234:5 + --> tests/ui/assigning_clones.rs:260:5 | LL | s = ToOwned::to_owned(&format!("{} {}", "hello", "world")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s)` -error: aborting due to 24 previous errors +error: aborting due to 30 previous errors diff --git a/tests/ui/bind_instead_of_map_multipart.stderr b/tests/ui/bind_instead_of_map_multipart.stderr index b15857c325ae5..2adaecc96d6a9 100644 --- a/tests/ui/bind_instead_of_map_multipart.stderr +++ b/tests/ui/bind_instead_of_map_multipart.stderr @@ -9,7 +9,7 @@ note: the lint level is defined here | LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try +help: use `map` instead | LL | let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); | ~~~ ~ ~~~~~~~ @@ -20,7 +20,7 @@ error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as LL | let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: try +help: use `map` instead | LL | let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); | ~~~ ~ ~~~~~~~ @@ -31,7 +31,7 @@ error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as LL | let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: try +help: use `map_err` instead | LL | let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() }); | ~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~ @@ -48,7 +48,7 @@ LL | | } LL | | }); | |______^ | -help: try +help: use `map` instead | LL ~ Some("42").map(|s| { LL | if { @@ -82,7 +82,7 @@ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed LL | let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: try +help: use `map` instead | LL | let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) }); | ~~~ ~~~~ ~~~~~~~~ diff --git a/tests/ui/crashes/ice-3717.stderr b/tests/ui/crashes/ice-3717.stderr index 01627ff5b94b9..4b4618ee1bbd0 100644 --- a/tests/ui/crashes/ice-3717.stderr +++ b/tests/ui/crashes/ice-3717.stderr @@ -9,14 +9,13 @@ note: the lint level is defined here | LL | #![deny(clippy::implicit_hasher)] | ^^^^^^^^^^^^^^^^^^^^^^^ -help: consider adding a type parameter +help: add a type parameter for `BuildHasher` | -LL | pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) { - | +++++++++++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~ -help: ...and use generic constructor +LL ~ pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) { +LL | +LL | let _ = [0u8; 0]; +LL ~ let _: HashSet<usize> = HashSet::default(); | -LL | let _: HashSet<usize> = HashSet::default(); - | ~~~~~~~~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/crashes/ice-6254.rs b/tests/ui/crashes/ice-6254.rs index 8af60890390e7..aaca32ab2d93e 100644 --- a/tests/ui/crashes/ice-6254.rs +++ b/tests/ui/crashes/ice-6254.rs @@ -2,7 +2,8 @@ // panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', // compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5 -#[allow(clippy::derive_partial_eq_without_eq)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::single_match)] + #[derive(PartialEq)] struct Foo(i32); const FOO_REF_REF: &&Foo = &&Foo(42); diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr index 9c6e640ca784c..ab51705bb55a5 100644 --- a/tests/ui/create_dir.stderr +++ b/tests/ui/create_dir.stderr @@ -2,16 +2,25 @@ error: calling `std::fs::create_dir` where there may be a better way --> tests/ui/create_dir.rs:10:5 | LL | std::fs::create_dir("foo"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("foo")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::create-dir` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::create_dir)]` +help: consider calling `std::fs::create_dir_all` instead + | +LL | create_dir_all("foo"); + | ~~~~~~~~~~~~~~~~~~~~~ error: calling `std::fs::create_dir` where there may be a better way --> tests/ui/create_dir.rs:11:5 | LL | std::fs::create_dir("bar").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("bar")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider calling `std::fs::create_dir_all` instead + | +LL | create_dir_all("bar").unwrap(); + | ~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 2 previous errors diff --git a/tests/ui/dbg_macro/dbg_macro.stderr b/tests/ui/dbg_macro/dbg_macro.stderr index 7d3c3f7c918e8..b3d74b9ff6173 100644 --- a/tests/ui/dbg_macro/dbg_macro.stderr +++ b/tests/ui/dbg_macro/dbg_macro.stderr @@ -81,7 +81,7 @@ error: the `dbg!` macro is intended as a debugging tool --> tests/ui/dbg_macro/dbg_macro.rs:48:5 | LL | dbg!(); - | ^^^^^^^ + | ^^^^^^ | help: remove the invocation before committing it to a version control system | @@ -136,7 +136,7 @@ error: the `dbg!` macro is intended as a debugging tool --> tests/ui/dbg_macro/dbg_macro.rs:43:13 | LL | dbg!(); - | ^^^^^^^ + | ^^^^^^ ... LL | expand_to_dbg!(); | ---------------- in this macro invocation diff --git a/tests/ui/dbg_macro/dbg_macro_unfixable.stderr b/tests/ui/dbg_macro/dbg_macro_unfixable.stderr index 16e51f4742e34..b8e91906b9317 100644 --- a/tests/ui/dbg_macro/dbg_macro_unfixable.stderr +++ b/tests/ui/dbg_macro/dbg_macro_unfixable.stderr @@ -2,7 +2,7 @@ error: the `dbg!` macro is intended as a debugging tool --> tests/ui/dbg_macro/auxiliary/submodule.rs:2:5 | LL | dbg!(); - | ^^^^^^^ + | ^^^^^^ | = note: `-D clippy::dbg-macro` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]` diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index d3c34fb371674..5617db90a470a 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -2,23 +2,18 @@ // Use that command to update this file and do not edit by hand. // Manual edits will be overwritten. -#![warn(clippy::should_assert_eq)] -#![warn(clippy::extend_from_slice)] -#![warn(clippy::range_step_by_zero)] -#![warn(clippy::unstable_as_slice)] -#![warn(clippy::unstable_as_mut_slice)] -#![warn(clippy::misaligned_transmute)] -#![warn(clippy::assign_ops)] -#![warn(clippy::if_let_redundant_pattern_matching)] -#![warn(clippy::unsafe_vector_initialization)] -#![warn(clippy::unused_collect)] -#![warn(clippy::replace_consts)] -#![warn(clippy::regex_macro)] -#![warn(clippy::find_map)] -#![warn(clippy::filter_map)] -#![warn(clippy::pub_enum_variant_names)] -#![warn(clippy::wrong_pub_self_convention)] -#![warn(clippy::maybe_misused_cfg)] -#![warn(clippy::mismatched_target_os)] +#![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq` +#![warn(clippy::extend_from_slice)] //~ ERROR: lint `clippy::extend_from_slice` +#![warn(clippy::range_step_by_zero)] //~ ERROR: lint `clippy::range_step_by_zero` +#![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice` +#![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice` +#![warn(clippy::misaligned_transmute)] //~ ERROR: lint `clippy::misaligned_transmute` +#![warn(clippy::assign_ops)] //~ ERROR: lint `clippy::assign_ops` +#![warn(clippy::unsafe_vector_initialization)] //~ ERROR: lint `clippy::unsafe_vector_initialization` +#![warn(clippy::unused_collect)] //~ ERROR: lint `clippy::unused_collect` +#![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts` +#![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro` +#![warn(clippy::pub_enum_variant_names)] //~ ERROR: lint `clippy::pub_enum_variant_names` +#![warn(clippy::wrong_pub_self_convention)] //~ ERROR: lint `clippy::wrong_pub_self_convention` fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 49b90c70c06ef..b3e1646c8045b 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -1,4 +1,4 @@ -error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011 +error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can --> tests/ui/deprecated.rs:5:9 | LL | #![warn(clippy::should_assert_eq)] @@ -7,107 +7,77 @@ LL | #![warn(clippy::should_assert_eq)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` -error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice +error: lint `clippy::extend_from_slice` has been removed: `Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization --> tests/ui/deprecated.rs:6:9 | LL | #![warn(clippy::extend_from_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays +error: lint `clippy::range_step_by_zero` has been removed: `Iterator::step_by(0)` now panics and is no longer an infinite iterator --> tests/ui/deprecated.rs:7:9 | LL | #![warn(clippy::range_step_by_zero)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7 +error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable --> tests/ui/deprecated.rs:8:9 | LL | #![warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7 +error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable --> tests/ui/deprecated.rs:9:9 | LL | #![warn(clippy::unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr +error: lint `clippy::misaligned_transmute` has been removed: split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr` --> tests/ui/deprecated.rs:10:9 | LL | #![warn(clippy::misaligned_transmute)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless +error: lint `clippy::assign_ops` has been removed: compound operators are harmless and linting on them is not in scope for clippy --> tests/ui/deprecated.rs:11:9 | LL | #![warn(clippy::assign_ops)] | ^^^^^^^^^^^^^^^^^^ -error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching +error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower --> tests/ui/deprecated.rs:12:9 | -LL | #![warn(clippy::if_let_redundant_pattern_matching)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior - --> tests/ui/deprecated.rs:13:9 - | LL | #![warn(clippy::unsafe_vector_initialization)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint - --> tests/ui/deprecated.rs:14:9 +error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]` + --> tests/ui/deprecated.rs:13:9 | LL | #![warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants - --> tests/ui/deprecated.rs:15:9 +error: lint `clippy::replace_consts` has been removed: `min_value` and `max_value` are now deprecated + --> tests/ui/deprecated.rs:14:9 | LL | #![warn(clippy::replace_consts)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018 - --> tests/ui/deprecated.rs:16:9 +error: lint `clippy::regex_macro` has been removed: the `regex!` macro was removed from the regex crate in 2018 + --> tests/ui/deprecated.rs:15:9 | LL | #![warn(clippy::regex_macro)] | ^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint - --> tests/ui/deprecated.rs:17:9 - | -LL | #![warn(clippy::find_map)] - | ^^^^^^^^^^^^^^^^ - -error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint - --> tests/ui/deprecated.rs:18:9 - | -LL | #![warn(clippy::filter_map)] - | ^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items - --> tests/ui/deprecated.rs:19:9 +error: lint `clippy::pub_enum_variant_names` has been removed: `clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config + --> tests/ui/deprecated.rs:16:9 | LL | #![warn(clippy::pub_enum_variant_names)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items - --> tests/ui/deprecated.rs:20:9 +error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config + --> tests/ui/deprecated.rs:17:9 | LL | #![warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::maybe_misused_cfg` has been removed: this lint has been replaced by `unexpected_cfgs` - --> tests/ui/deprecated.rs:21:9 - | -LL | #![warn(clippy::maybe_misused_cfg)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: lint `clippy::mismatched_target_os` has been removed: this lint has been replaced by `unexpected_cfgs` - --> tests/ui/deprecated.rs:22:9 - | -LL | #![warn(clippy::mismatched_target_os)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 18 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/deprecated_old.rs b/tests/ui/deprecated_old.rs deleted file mode 100644 index 356ad5f060b6c..0000000000000 --- a/tests/ui/deprecated_old.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[warn(unstable_as_slice)] -//~^ ERROR: lint `unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized -//~| NOTE: `-D renamed-and-removed-lints` implied by `-D warnings` -#[warn(unstable_as_mut_slice)] -//~^ ERROR: lint `unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been st -#[warn(misaligned_transmute)] -//~^ ERROR: lint `misaligned_transmute` has been removed: this lint has been split into ca - -fn main() {} diff --git a/tests/ui/deprecated_old.stderr b/tests/ui/deprecated_old.stderr deleted file mode 100644 index 685bca64df59a..0000000000000 --- a/tests/ui/deprecated_old.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: lint `unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7 - --> tests/ui/deprecated_old.rs:1:8 - | -LL | #[warn(unstable_as_slice)] - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` - -error: lint `unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7 - --> tests/ui/deprecated_old.rs:4:8 - | -LL | #[warn(unstable_as_mut_slice)] - | ^^^^^^^^^^^^^^^^^^^^^ - -error: lint `misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr - --> tests/ui/deprecated_old.rs:6:8 - | -LL | #[warn(misaligned_transmute)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui/deref_by_slicing.fixed b/tests/ui/deref_by_slicing.fixed index a3c2e84566681..87b33b1f881d8 100644 --- a/tests/ui/deref_by_slicing.fixed +++ b/tests/ui/deref_by_slicing.fixed @@ -25,4 +25,8 @@ fn main() { let bytes: &[u8] = &[]; let _ = (&*bytes).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice + + // issue 12751 + let a = &mut [1, 2, 3][..]; + let _ = &*a; } diff --git a/tests/ui/deref_by_slicing.rs b/tests/ui/deref_by_slicing.rs index 5b4a73712ee6e..8d8882a1781e8 100644 --- a/tests/ui/deref_by_slicing.rs +++ b/tests/ui/deref_by_slicing.rs @@ -25,4 +25,8 @@ fn main() { let bytes: &[u8] = &[]; let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice + + // issue 12751 + let a = &mut [1, 2, 3][..]; + let _ = &a[..]; } diff --git a/tests/ui/deref_by_slicing.stderr b/tests/ui/deref_by_slicing.stderr index 17b00610899d6..ceb9ab6db73dc 100644 --- a/tests/ui/deref_by_slicing.stderr +++ b/tests/ui/deref_by_slicing.stderr @@ -55,5 +55,11 @@ error: slicing when dereferencing would work LL | let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice | ^^^^^^^^^^^^ help: reborrow the original value instead: `(&*bytes)` -error: aborting due to 9 previous errors +error: slicing when dereferencing would work + --> tests/ui/deref_by_slicing.rs:31:13 + | +LL | let _ = &a[..]; + | ^^^^^^ help: reborrow the original value instead: `&*a` + +error: aborting due to 10 previous errors diff --git a/tests/ui/doc/footnote_issue_13183.rs b/tests/ui/doc/footnote_issue_13183.rs new file mode 100644 index 0000000000000..a18b4c3ac539b --- /dev/null +++ b/tests/ui/doc/footnote_issue_13183.rs @@ -0,0 +1,10 @@ +// This is a regression test for <https://github.com/rust-lang/rust-clippy/issues/13183>. +// It should not warn on missing backticks on footnote references. + +#![warn(clippy::doc_markdown)] +// Should not warn! +//! Here's a footnote[^example_footnote_identifier] +//! +//! [^example_footnote_identifier]: This is merely an example. + +fn main() {} diff --git a/tests/ui/empty_drop.stderr b/tests/ui/empty_drop.stderr index 4223ddaf3fba5..d4d020fec30cf 100644 --- a/tests/ui/empty_drop.stderr +++ b/tests/ui/empty_drop.stderr @@ -4,10 +4,11 @@ error: empty drop implementation LL | / impl Drop for Foo { LL | | fn drop(&mut self) {} LL | | } - | |_^ help: try removing this impl + | |_^ | = note: `-D clippy::empty-drop` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::empty_drop)]` + = help: try removing this impl error: empty drop implementation --> tests/ui/empty_drop.rs:23:1 @@ -17,7 +18,9 @@ LL | | fn drop(&mut self) { LL | | {} LL | | } LL | | } - | |_^ help: try removing this impl + | |_^ + | + = help: try removing this impl error: aborting due to 2 previous errors diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 7126f2799455f..ca422ee29c104 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -2,6 +2,7 @@ #![allow(unused)] #![allow( clippy::needless_borrow, + clippy::needless_option_as_deref, clippy::needless_pass_by_value, clippy::no_effect, clippy::option_map_unit_fn, @@ -482,3 +483,19 @@ mod issue_12853 { x(); } } + +mod issue_13073 { + fn get_default() -> Option<&'static str> { + Some("foo") + } + + pub fn foo() { + // shouldn't lint + let bind: Option<String> = None; + let _field = bind.as_deref().or_else(|| get_default()).unwrap(); + let bind: Option<&'static str> = None; + let _field = bind.as_deref().or_else(|| get_default()).unwrap(); + // should lint + let _field = bind.or_else(get_default).unwrap(); + } +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 0787abf5f3e37..c0db91c03ef45 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -2,6 +2,7 @@ #![allow(unused)] #![allow( clippy::needless_borrow, + clippy::needless_option_as_deref, clippy::needless_pass_by_value, clippy::no_effect, clippy::option_map_unit_fn, @@ -482,3 +483,19 @@ mod issue_12853 { x(); } } + +mod issue_13073 { + fn get_default() -> Option<&'static str> { + Some("foo") + } + + pub fn foo() { + // shouldn't lint + let bind: Option<String> = None; + let _field = bind.as_deref().or_else(|| get_default()).unwrap(); + let bind: Option<&'static str> = None; + let _field = bind.as_deref().or_else(|| get_default()).unwrap(); + // should lint + let _field = bind.or_else(|| get_default()).unwrap(); + } +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index c757601042f16..5540261fc5744 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -1,5 +1,5 @@ error: redundant closure - --> tests/ui/eta.rs:29:27 + --> tests/ui/eta.rs:30:27 | LL | let a = Some(1u8).map(|a| foo(a)); | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` @@ -8,31 +8,31 @@ LL | let a = Some(1u8).map(|a| foo(a)); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` error: redundant closure - --> tests/ui/eta.rs:33:40 + --> tests/ui/eta.rs:34:40 | LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec! | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` error: redundant closure - --> tests/ui/eta.rs:34:35 + --> tests/ui/eta.rs:35:35 | LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2` error: redundant closure - --> tests/ui/eta.rs:35:26 + --> tests/ui/eta.rs:36:26 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` error: redundant closure - --> tests/ui/eta.rs:42:27 + --> tests/ui/eta.rs:43:27 | LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` error: redundant closure - --> tests/ui/eta.rs:94:51 + --> tests/ui/eta.rs:95:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` @@ -41,166 +41,172 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_for_method_calls)]` error: redundant closure - --> tests/ui/eta.rs:95:51 + --> tests/ui/eta.rs:96:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` error: redundant closure - --> tests/ui/eta.rs:97:42 + --> tests/ui/eta.rs:98:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` error: redundant closure - --> tests/ui/eta.rs:101:29 + --> tests/ui/eta.rs:102:29 | LL | let e = Some("str").map(|s| s.to_string()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` error: redundant closure - --> tests/ui/eta.rs:102:27 + --> tests/ui/eta.rs:103:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` error: redundant closure - --> tests/ui/eta.rs:104:65 + --> tests/ui/eta.rs:105:65 | LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> tests/ui/eta.rs:167:22 + --> tests/ui/eta.rs:168:22 | LL | requires_fn_once(|| x()); | ^^^^^^ help: replace the closure with the function itself: `x` error: redundant closure - --> tests/ui/eta.rs:174:27 + --> tests/ui/eta.rs:175:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> tests/ui/eta.rs:179:27 + --> tests/ui/eta.rs:180:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` error: redundant closure - --> tests/ui/eta.rs:211:28 + --> tests/ui/eta.rs:212:28 | LL | x.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> tests/ui/eta.rs:212:28 + --> tests/ui/eta.rs:213:28 | LL | y.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> tests/ui/eta.rs:213:28 + --> tests/ui/eta.rs:214:28 | LL | z.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res` error: redundant closure - --> tests/ui/eta.rs:220:21 + --> tests/ui/eta.rs:221:21 | LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> tests/ui/eta.rs:224:21 + --> tests/ui/eta.rs:225:21 | LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` error: redundant closure - --> tests/ui/eta.rs:317:18 + --> tests/ui/eta.rs:318:18 | LL | takes_fn_mut(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> tests/ui/eta.rs:320:19 + --> tests/ui/eta.rs:321:19 | LL | takes_fn_once(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> tests/ui/eta.rs:324:26 + --> tests/ui/eta.rs:325:26 | LL | move || takes_fn_mut(|| f_used_once()) | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` error: redundant closure - --> tests/ui/eta.rs:336:19 + --> tests/ui/eta.rs:337:19 | LL | array_opt.map(|a| a.as_slice()); | ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice` error: redundant closure - --> tests/ui/eta.rs:339:19 + --> tests/ui/eta.rs:340:19 | LL | slice_opt.map(|s| s.len()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len` error: redundant closure - --> tests/ui/eta.rs:342:17 + --> tests/ui/eta.rs:343:17 | LL | ptr_opt.map(|p| p.is_null()); | ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null` error: redundant closure - --> tests/ui/eta.rs:346:17 + --> tests/ui/eta.rs:347:17 | LL | dyn_opt.map(|d| d.method_on_dyn()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<dyn TestTrait>::method_on_dyn` error: redundant closure - --> tests/ui/eta.rs:406:19 + --> tests/ui/eta.rs:407:19 | LL | let _ = f(&0, |x, y| f2(x, y)); | ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2` error: redundant closure - --> tests/ui/eta.rs:434:22 + --> tests/ui/eta.rs:435:22 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Test::method` error: redundant closure - --> tests/ui/eta.rs:438:22 + --> tests/ui/eta.rs:439:22 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `super::Outer::method` error: redundant closure - --> tests/ui/eta.rs:451:18 + --> tests/ui/eta.rs:452:18 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `test_mod::Test::method` error: redundant closure - --> tests/ui/eta.rs:458:30 + --> tests/ui/eta.rs:459:30 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method` error: redundant closure - --> tests/ui/eta.rs:477:38 + --> tests/ui/eta.rs:478:38 | LL | let x = Box::new(|| None.map(|x| f(x))); | ^^^^^^^^ help: replace the closure with the function itself: `&f` error: redundant closure - --> tests/ui/eta.rs:481:38 + --> tests/ui/eta.rs:482:38 | LL | let x = Box::new(|| None.map(|x| f(x))); | ^^^^^^^^ help: replace the closure with the function itself: `f` -error: aborting due to 33 previous errors +error: redundant closure + --> tests/ui/eta.rs:499:35 + | +LL | let _field = bind.or_else(|| get_default()).unwrap(); + | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default` + +error: aborting due to 34 previous errors diff --git a/tests/ui/excessive_precision.stderr b/tests/ui/excessive_precision.stderr index 6d8e166a649d8..81e4fb6765d0b 100644 --- a/tests/ui/excessive_precision.stderr +++ b/tests/ui/excessive_precision.stderr @@ -2,100 +2,179 @@ error: float has excessive precision --> tests/ui/excessive_precision.rs:20:26 | LL | const BAD32_1: f32 = 0.123_456_789_f32; - | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79_f32` + | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::excessive-precision` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::excessive_precision)]` +help: consider changing the type or truncating it to + | +LL | const BAD32_1: f32 = 0.123_456_79_f32; + | ~~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:21:26 | LL | const BAD32_2: f32 = 0.123_456_789; - | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79` + | ^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | const BAD32_2: f32 = 0.123_456_79; + | ~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:22:26 | LL | const BAD32_3: f32 = 0.100_000_000_000_1; - | ^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1` + | ^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | const BAD32_3: f32 = 0.1; + | ~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:23:29 | LL | const BAD32_EDGE: f32 = 1.000_000_9; - | ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001` + | ^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | const BAD32_EDGE: f32 = 1.000_001; + | ~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:27:26 | LL | const BAD64_3: f64 = 0.100_000_000_000_000_000_1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | const BAD64_3: f64 = 0.1; + | ~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:30:22 | LL | println!("{:?}", 8.888_888_888_888_888_888_888); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `8.888_888_888_888_89` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | println!("{:?}", 8.888_888_888_888_89); + | ~~~~~~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:41:22 | LL | let bad32: f32 = 1.123_456_789; - | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8` + | ^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad32: f32 = 1.123_456_8; + | ~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:42:26 | LL | let bad32_suf: f32 = 1.123_456_789_f32; - | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32` + | ^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad32_suf: f32 = 1.123_456_8_f32; + | ~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:43:21 | LL | let bad32_inf = 1.123_456_789_f32; - | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32` + | ^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad32_inf = 1.123_456_8_f32; + | ~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:53:36 | LL | let bad_vec32: Vec<f32> = vec![0.123_456_789]; - | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79` + | ^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad_vec32: Vec<f32> = vec![0.123_456_79]; + | ~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:54:36 | LL | let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_123_456_78` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_78]; + | ~~~~~~~~~~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:58:24 | LL | let bad_e32: f32 = 1.123_456_788_888e-10; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8e-10` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad_e32: f32 = 1.123_456_8e-10; + | ~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:61:27 | LL | let bad_bige32: f32 = 1.123_456_788_888E-10; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let bad_bige32: f32 = 1.123_456_8E-10; + | ~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:70:13 | LL | let _ = 2.225_073_858_507_201_1e-308_f64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `2.225_073_858_507_201e-308_f64` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let _ = 2.225_073_858_507_201e-308_f64; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:73:13 | LL | let _ = 1.000_000_000_000_001e-324_f64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0_f64` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | let _ = 0_f64; + | ~~~~~ error: float has excessive precision --> tests/ui/excessive_precision.rs:83:20 | LL | const _: f64 = 3.0000000000000000e+00; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `3.0` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or truncating it to + | +LL | const _: f64 = 3.0; + | ~~~ error: aborting due to 16 previous errors diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index c25e79a36171f..28b477b69215b 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -278,3 +278,16 @@ mod issue_10058 { } } } + +mod issue_13123 { + pub fn test() { + let mut vec = vec![1, 2, 3, 4]; + let mut _index = 0; + 'label: for v in vec { + _index += 1; + if v == 1 { + break 'label; + } + } + } +} diff --git a/tests/ui/explicit_counter_loop.stderr b/tests/ui/explicit_counter_loop.stderr index e28f8783f9c36..1b2d1f8570a3e 100644 --- a/tests/ui/explicit_counter_loop.stderr +++ b/tests/ui/explicit_counter_loop.stderr @@ -57,5 +57,11 @@ LL | for _item in slice { | = note: `idx_u32` is of type `u32`, making it ineligible for `Iterator::enumerate` -error: aborting due to 9 previous errors +error: the variable `_index` is used as a loop counter + --> tests/ui/explicit_counter_loop.rs:286:9 + | +LL | 'label: for v in vec { + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `'label: for (_index, v) in vec.into_iter().enumerate()` + +error: aborting due to 10 previous errors diff --git a/tests/ui/fn_to_numeric_cast_any.stderr b/tests/ui/fn_to_numeric_cast_any.stderr index e5bb8d1326975..a05b7138bc99b 100644 --- a/tests/ui/fn_to_numeric_cast_any.stderr +++ b/tests/ui/fn_to_numeric_cast_any.stderr @@ -2,106 +2,190 @@ error: casting function pointer `foo` to `i8` --> tests/ui/fn_to_numeric_cast_any.rs:23:13 | LL | let _ = foo as i8; - | ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i8` + | ^^^^^^^^^ | = note: `-D clippy::fn-to-numeric-cast-any` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_any)]` +help: did you mean to invoke the function? + | +LL | let _ = foo() as i8; + | ~~~~~~~~~~~ error: casting function pointer `foo` to `i16` --> tests/ui/fn_to_numeric_cast_any.rs:26:13 | LL | let _ = foo as i16; - | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i16` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as i16; + | ~~~~~~~~~~~~ error: casting function pointer `foo` to `i32` --> tests/ui/fn_to_numeric_cast_any.rs:28:13 | LL | let _ = foo as i32; - | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i32` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as i32; + | ~~~~~~~~~~~~ error: casting function pointer `foo` to `i64` --> tests/ui/fn_to_numeric_cast_any.rs:30:13 | LL | let _ = foo as i64; - | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i64` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as i64; + | ~~~~~~~~~~~~ error: casting function pointer `foo` to `i128` --> tests/ui/fn_to_numeric_cast_any.rs:32:13 | LL | let _ = foo as i128; - | ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i128` + | ^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as i128; + | ~~~~~~~~~~~~~ error: casting function pointer `foo` to `isize` --> tests/ui/fn_to_numeric_cast_any.rs:34:13 | LL | let _ = foo as isize; - | ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as isize` + | ^^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as isize; + | ~~~~~~~~~~~~~~ error: casting function pointer `foo` to `u8` --> tests/ui/fn_to_numeric_cast_any.rs:37:13 | LL | let _ = foo as u8; - | ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u8` + | ^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as u8; + | ~~~~~~~~~~~ error: casting function pointer `foo` to `u16` --> tests/ui/fn_to_numeric_cast_any.rs:39:13 | LL | let _ = foo as u16; - | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u16` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as u16; + | ~~~~~~~~~~~~ error: casting function pointer `foo` to `u32` --> tests/ui/fn_to_numeric_cast_any.rs:41:13 | LL | let _ = foo as u32; - | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u32` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as u32; + | ~~~~~~~~~~~~ error: casting function pointer `foo` to `u64` --> tests/ui/fn_to_numeric_cast_any.rs:43:13 | LL | let _ = foo as u64; - | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u64` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as u64; + | ~~~~~~~~~~~~ error: casting function pointer `foo` to `u128` --> tests/ui/fn_to_numeric_cast_any.rs:45:13 | LL | let _ = foo as u128; - | ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u128` + | ^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as u128; + | ~~~~~~~~~~~~~ error: casting function pointer `foo` to `usize` --> tests/ui/fn_to_numeric_cast_any.rs:47:13 | LL | let _ = foo as usize; - | ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as usize` + | ^^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as usize; + | ~~~~~~~~~~~~~~ error: casting function pointer `Struct::static_method` to `usize` --> tests/ui/fn_to_numeric_cast_any.rs:52:13 | LL | let _ = Struct::static_method as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `Struct::static_method() as usize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = Struct::static_method() as usize; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting function pointer `f` to `usize` --> tests/ui/fn_to_numeric_cast_any.rs:57:5 | LL | f as usize - | ^^^^^^^^^^ help: did you mean to invoke the function?: `f() as usize` + | ^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | f() as usize + | error: casting function pointer `T::static_method` to `usize` --> tests/ui/fn_to_numeric_cast_any.rs:62:5 | LL | T::static_method as usize - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `T::static_method() as usize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | T::static_method() as usize + | error: casting function pointer `(clos as fn(u32) -> u32)` to `usize` --> tests/ui/fn_to_numeric_cast_any.rs:69:13 | LL | let _ = (clos as fn(u32) -> u32) as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `(clos as fn(u32) -> u32)() as usize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = (clos as fn(u32) -> u32)() as usize; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting function pointer `foo` to `*const ()` --> tests/ui/fn_to_numeric_cast_any.rs:74:13 | LL | let _ = foo as *const (); - | ^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as *const ()` + | ^^^^^^^^^^^^^^^^ + | +help: did you mean to invoke the function? + | +LL | let _ = foo() as *const (); + | ~~~~~~~~~~~~~~~~~~ error: aborting due to 17 previous errors diff --git a/tests/ui/for_kv_map.fixed b/tests/ui/for_kv_map.fixed index a2112d7b73005..1733b29128fe5 100644 --- a/tests/ui/for_kv_map.fixed +++ b/tests/ui/for_kv_map.fixed @@ -40,6 +40,16 @@ fn main() { let _k = k; } + let m: HashMap<u64, u64> = HashMap::new(); + let rm = &m; + 'label: for k in rm.keys() { + //~^ ERROR: you seem to want to iterate on a map's keys + let _k = k; + if *k == 0u64 { + break 'label; + } + } + // The following should not produce warnings. let m: HashMap<u64, u64> = HashMap::new(); diff --git a/tests/ui/for_kv_map.rs b/tests/ui/for_kv_map.rs index 1b7959b8f92d6..de465a7c8e6e3 100644 --- a/tests/ui/for_kv_map.rs +++ b/tests/ui/for_kv_map.rs @@ -40,6 +40,16 @@ fn main() { let _k = k; } + let m: HashMap<u64, u64> = HashMap::new(); + let rm = &m; + 'label: for (k, _value) in rm { + //~^ ERROR: you seem to want to iterate on a map's keys + let _k = k; + if *k == 0u64 { + break 'label; + } + } + // The following should not produce warnings. let m: HashMap<u64, u64> = HashMap::new(); diff --git a/tests/ui/for_kv_map.stderr b/tests/ui/for_kv_map.stderr index f4ce473d095bf..adcc3ab8fdb90 100644 --- a/tests/ui/for_kv_map.stderr +++ b/tests/ui/for_kv_map.stderr @@ -55,5 +55,16 @@ help: use the corresponding method LL | for k in rm.keys() { | ~ ~~~~~~~~~ -error: aborting due to 5 previous errors +error: you seem to want to iterate on a map's keys + --> tests/ui/for_kv_map.rs:45:32 + | +LL | 'label: for (k, _value) in rm { + | ^^ + | +help: use the corresponding method + | +LL | 'label: for k in rm.keys() { + | ~ ~~~~~~~~~ + +error: aborting due to 6 previous errors diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index a08b6657dcc7c..0f8b279da1e7a 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -1,14 +1,18 @@ -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui/get_unwrap.rs:37:17 | LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> tests/ui/get_unwrap.rs:9:9 | LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ +help: using `[]` is clearer and more concise + | +LL | let _ = &boxed_slice[1]; + | ~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:37:17 @@ -21,11 +25,16 @@ LL | let _ = boxed_slice.get(1).unwrap(); = note: `-D clippy::unwrap-used` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unwrap_used)]` -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui/get_unwrap.rs:38:17 | LL | let _ = some_slice.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_slice[0]; + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:38:17 @@ -36,11 +45,16 @@ LL | let _ = some_slice.get(0).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a Vec --> tests/ui/get_unwrap.rs:39:17 | LL | let _ = some_vec.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vec[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_vec[0]; + | ~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:39:17 @@ -51,11 +65,16 @@ LL | let _ = some_vec.get(0).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a VecDeque --> tests/ui/get_unwrap.rs:40:17 | LL | let _ = some_vecdeque.get(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vecdeque[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_vecdeque[0]; + | ~~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:40:17 @@ -66,11 +85,16 @@ LL | let _ = some_vecdeque.get(0).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a HashMap --> tests/ui/get_unwrap.rs:41:17 | LL | let _ = some_hashmap.get(&1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_hashmap[&1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_hashmap[&1]; + | ~~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:41:17 @@ -81,11 +105,16 @@ LL | let _ = some_hashmap.get(&1).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a BTreeMap --> tests/ui/get_unwrap.rs:42:17 | LL | let _ = some_btreemap.get(&1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_btreemap[&1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = &some_btreemap[&1]; + | ~~~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:42:17 @@ -96,11 +125,16 @@ LL | let _ = some_btreemap.get(&1).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui/get_unwrap.rs:46:21 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _: u8 = boxed_slice[1]; + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:46:22 @@ -111,11 +145,16 @@ LL | let _: u8 = *boxed_slice.get(1).unwrap(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a slice --> tests/ui/get_unwrap.rs:51:9 | LL | *boxed_slice.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | boxed_slice[0] = 1; + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:51:10 @@ -126,11 +165,16 @@ LL | *boxed_slice.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a slice --> tests/ui/get_unwrap.rs:52:9 | LL | *some_slice.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_slice[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | some_slice[0] = 1; + | ~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:52:10 @@ -141,11 +185,16 @@ LL | *some_slice.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a Vec --> tests/ui/get_unwrap.rs:53:9 | LL | *some_vec.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | some_vec[0] = 1; + | ~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:53:10 @@ -156,11 +205,16 @@ LL | *some_vec.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a VecDeque --> tests/ui/get_unwrap.rs:54:9 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vecdeque[0]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | some_vecdeque[0] = 1; + | ~~~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:54:10 @@ -171,11 +225,16 @@ LL | *some_vecdeque.get_mut(0).unwrap() = 1; = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a Vec --> tests/ui/get_unwrap.rs:66:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = some_vec[0..1].to_vec(); + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:66:17 @@ -186,11 +245,16 @@ LL | let _ = some_vec.get(0..1).unwrap().to_vec(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a Vec --> tests/ui/get_unwrap.rs:67:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _ = some_vec[0..1].to_vec(); + | ~~~~~~~~~~~~~~ error: used `unwrap()` on an `Option` value --> tests/ui/get_unwrap.rs:67:17 @@ -201,29 +265,49 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui/get_unwrap.rs:77:24 | LL | let _x: &i32 = f.get(1 + 2).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `&f[1 + 2]` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _x: &i32 = &f[1 + 2]; + | ~~~~~~~~~ -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui/get_unwrap.rs:80:18 | LL | let _x = f.get(1 + 2).unwrap().to_string(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `f[1 + 2]` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _x = f[1 + 2].to_string(); + | ~~~~~~~~ -error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get().unwrap()` on a slice --> tests/ui/get_unwrap.rs:83:18 | LL | let _x = f.get(1 + 2).unwrap().abs(); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `f[1 + 2]` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let _x = f[1 + 2].abs(); + | ~~~~~~~~ -error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise +error: called `.get_mut().unwrap()` on a slice --> tests/ui/get_unwrap.rs:100:33 | LL | let b = rest.get_mut(linidx(j, k) - linidx(i, k) - 1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut rest[linidx(j, k) - linidx(i, k) - 1]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: using `[]` is clearer and more concise + | +LL | let b = &mut rest[linidx(j, k) - linidx(i, k) - 1]; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 30 previous errors diff --git a/tests/ui/if_let_mutex.rs b/tests/ui/if_let_mutex.rs index cb6915e0ebaaa..bb0eadfca1c7a 100644 --- a/tests/ui/if_let_mutex.rs +++ b/tests/ui/if_let_mutex.rs @@ -1,4 +1,5 @@ #![warn(clippy::if_let_mutex)] +#![allow(clippy::redundant_pattern_matching)] use std::ops::Deref; use std::sync::Mutex; @@ -50,4 +51,12 @@ fn mutex_ref(mutex: &Mutex<i32>) { }; } +fn multiple_mutexes(m1: &Mutex<()>, m2: &Mutex<()>) { + if let Ok(_) = m1.lock() { + m2.lock(); + } else { + m1.lock(); + } +} + fn main() {} diff --git a/tests/ui/if_let_mutex.stderr b/tests/ui/if_let_mutex.stderr index 6e0115c23af68..45df4ac4d6795 100644 --- a/tests/ui/if_let_mutex.stderr +++ b/tests/ui/if_let_mutex.stderr @@ -1,5 +1,5 @@ error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock - --> tests/ui/if_let_mutex.rs:10:5 + --> tests/ui/if_let_mutex.rs:11:5 | LL | if let Err(locked) = m.lock() { | ^ - this Mutex will remain locked for the entire `if let`-block... @@ -19,7 +19,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::if_let_mutex)]` error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock - --> tests/ui/if_let_mutex.rs:23:5 + --> tests/ui/if_let_mutex.rs:24:5 | LL | if let Some(locked) = m.lock().unwrap().deref() { | ^ - this Mutex will remain locked for the entire `if let`-block... @@ -37,7 +37,7 @@ LL | | }; = help: move the lock call outside of the `if let ...` expression error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock - --> tests/ui/if_let_mutex.rs:45:5 + --> tests/ui/if_let_mutex.rs:46:5 | LL | if let Ok(i) = mutex.lock() { | ^ ----- this Mutex will remain locked for the entire `if let`-block... @@ -53,5 +53,21 @@ LL | | }; | = help: move the lock call outside of the `if let ...` expression -error: aborting due to 3 previous errors +error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock + --> tests/ui/if_let_mutex.rs:55:5 + | +LL | if let Ok(_) = m1.lock() { + | ^ -- this Mutex will remain locked for the entire `if let`-block... + | _____| + | | +LL | | m2.lock(); +LL | | } else { +LL | | m1.lock(); + | | -- ... and is tried to lock again here, which will always deadlock. +LL | | } + | |_____^ + | + = help: move the lock call outside of the `if let ...` expression + +error: aborting due to 4 previous errors diff --git a/tests/ui/if_then_some_else_none.fixed b/tests/ui/if_then_some_else_none.fixed new file mode 100644 index 0000000000000..ad13372a68b7e --- /dev/null +++ b/tests/ui/if_then_some_else_none.fixed @@ -0,0 +1,119 @@ +#![warn(clippy::if_then_some_else_none)] +#![allow(clippy::redundant_pattern_matching, clippy::unnecessary_lazy_evaluations)] + +fn main() { + // Should issue an error. + let _ = foo().then(|| { println!("true!"); "foo" }); + + // Should issue an error when macros are used. + let _ = matches!(true, true).then(|| { println!("true!"); matches!(true, false) }); + + // Should issue an error. Binary expression `o < 32` should be parenthesized. + let x = Some(5); + let _ = x.and_then(|o| (o < 32).then_some(o)); + //~^ ERROR: this could be simplified with `bool::then_some` + + // Should issue an error. Unary expression `!x` should be parenthesized. + let x = true; + let _ = (!x).then_some(0); + //~^ ERROR: this could be simplified with `bool::then_some` + + // Should not issue an error since the `else` block has a statement besides `None`. + let _ = if foo() { + println!("true!"); + Some("foo") + } else { + eprintln!("false..."); + None + }; + + // Should not issue an error since there are more than 2 blocks in the if-else chain. + let _ = if foo() { + println!("foo true!"); + Some("foo") + } else if bar() { + println!("bar true!"); + Some("bar") + } else { + None + }; + + let _ = if foo() { + println!("foo true!"); + Some("foo") + } else { + bar().then(|| { + println!("bar true!"); + "bar" + }) + }; + + // Should not issue an error since the `then` block has `None`, not `Some`. + let _ = if foo() { None } else { Some("foo is false") }; + + // Should not issue an error since the `else` block doesn't use `None` directly. + let _ = if foo() { Some("foo is true") } else { into_none() }; + + // Should not issue an error since the `then` block doesn't use `Some` directly. + let _ = if foo() { into_some("foo") } else { None }; +} + +#[clippy::msrv = "1.49"] +fn _msrv_1_49() { + // `bool::then` was stabilized in 1.50. Do not lint this + let _ = if foo() { + println!("true!"); + Some(149) + } else { + None + }; +} + +#[clippy::msrv = "1.50"] +fn _msrv_1_50() { + let _ = foo().then(|| { println!("true!"); 150 }); +} + +fn foo() -> bool { + unimplemented!() +} + +fn bar() -> bool { + unimplemented!() +} + +fn into_some<T>(v: T) -> Option<T> { + Some(v) +} + +fn into_none<T>() -> Option<T> { + None +} + +// Should not warn +fn f(b: bool, v: Option<()>) -> Option<()> { + if b { + v?; // This is a potential early return, is not equivalent with `bool::then` + + Some(()) + } else { + None + } +} + +fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> { + let x = if b { + #[allow(clippy::let_unit_value)] + let _ = v?; + Some(()) + } else { + None + }; + + Ok(()) +} + +const fn issue12103(x: u32) -> Option<u32> { + // Should not issue an error in `const` context + if x > 42 { Some(150) } else { None } +} diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index ccde154bd56ca..73edbb7da2a87 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -1,5 +1,5 @@ #![warn(clippy::if_then_some_else_none)] -#![allow(clippy::redundant_pattern_matching)] +#![allow(clippy::redundant_pattern_matching, clippy::unnecessary_lazy_evaluations)] fn main() { // Should issue an error. diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr index e0a95aebdc134..aed01e026cbe7 100644 --- a/tests/ui/if_then_some_else_none.stderr +++ b/tests/ui/if_then_some_else_none.stderr @@ -9,9 +9,8 @@ LL | | Some("foo") LL | | } else { LL | | None LL | | }; - | |_____^ + | |_____^ help: try: `foo().then(|| { println!("true!"); "foo" })` | - = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ "foo" })` = note: `-D clippy::if-then-some-else-none` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::if_then_some_else_none)]` @@ -26,25 +25,19 @@ LL | | Some(matches!(true, false)) LL | | } else { LL | | None LL | | }; - | |_____^ - | - = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })` + | |_____^ help: try: `matches!(true, true).then(|| { println!("true!"); matches!(true, false) })` error: this could be simplified with `bool::then_some` --> tests/ui/if_then_some_else_none.rs:25:28 | LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using `bool::then_some` like: `(o < 32).then_some(o)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(o < 32).then_some(o)` error: this could be simplified with `bool::then_some` --> tests/ui/if_then_some_else_none.rs:30:13 | LL | let _ = if !x { Some(0) } else { None }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using `bool::then_some` like: `(!x).then_some(0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(!x).then_some(0)` error: this could be simplified with `bool::then` --> tests/ui/if_then_some_else_none.rs:86:13 @@ -57,9 +50,7 @@ LL | | Some(150) LL | | } else { LL | | None LL | | }; - | |_____^ - | - = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ 150 })` + | |_____^ help: try: `foo().then(|| { println!("true!"); 150 })` error: aborting due to 5 previous errors diff --git a/tests/ui/implicit_hasher.fixed b/tests/ui/implicit_hasher.fixed new file mode 100644 index 0000000000000..2d6dc0274cf2a --- /dev/null +++ b/tests/ui/implicit_hasher.fixed @@ -0,0 +1,102 @@ +//@aux-build:proc_macros.rs +#![deny(clippy::implicit_hasher)] + +#[macro_use] +extern crate proc_macros; + +use std::cmp::Eq; +use std::collections::{HashMap, HashSet}; +use std::hash::{BuildHasher, Hash}; + +pub trait Foo<T>: Sized { + fn make() -> (Self, Self); +} + +impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> { + fn make() -> (Self, Self) { + // OK, don't suggest to modify these + let _: HashMap<i32, i32> = HashMap::new(); + let _: HashSet<i32> = HashSet::new(); + + (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + } +} +impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) { + fn make() -> (Self, Self) { + ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),)) + } +} +impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> { + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + } +} + +impl<K: Hash + Eq, V, S: BuildHasher + Default> Foo<i32> for HashMap<K, V, S> { + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default())) + } +} +impl<S: BuildHasher + Default> Foo<i64> for HashMap<String, String, S> { + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default())) + } +} + +impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> { + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default())) + } +} +impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> { + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default())) + } +} + +impl<T: Hash + Eq, S: BuildHasher + Default> Foo<i32> for HashSet<T, S> { + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default())) + } +} +impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> { + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default())) + } +} + +pub fn map<S: ::std::hash::BuildHasher>(map: &mut HashMap<i32, i32, S>) {} + +pub fn set<S: ::std::hash::BuildHasher>(set: &mut HashSet<i32, S>) {} + +#[inline_macros] +pub mod gen { + use super::*; + inline! { + impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> { + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + } + } + + pub fn bar(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {} + } +} + +// When the macro is in a different file, the suggestion spans can't be combined properly +// and should not cause an ICE +// See #2707 +#[macro_use] +#[path = "auxiliary/test_macro.rs"] +pub mod test_macro; +__implicit_hasher_test_macro!(impl<K, V> for HashMap<K, V> where V: test_macro::A); + +// #4260 +external! { + pub fn f(input: &HashMap<u32, u32>) {} +} + +// #7712 +pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {} + +fn main() {} diff --git a/tests/ui/implicit_hasher.rs b/tests/ui/implicit_hasher.rs index f7cd541741b11..0a334357bd1b8 100644 --- a/tests/ui/implicit_hasher.rs +++ b/tests/ui/implicit_hasher.rs @@ -1,11 +1,8 @@ //@aux-build:proc_macros.rs -//@no-rustfix #![deny(clippy::implicit_hasher)] -#![allow(unused)] #[macro_use] extern crate proc_macros; -use proc_macros::external; use std::cmp::Eq; use std::collections::{HashMap, HashSet}; @@ -68,9 +65,11 @@ impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> { } } -pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {} +pub fn map(map: &mut HashMap<i32, i32>) {} -#[proc_macros::inline_macros] +pub fn set(set: &mut HashSet<i32>) {} + +#[inline_macros] pub mod gen { use super::*; inline! { diff --git a/tests/ui/implicit_hasher.stderr b/tests/ui/implicit_hasher.stderr index a3df8edf389be..48c6ebc209cf3 100644 --- a/tests/ui/implicit_hasher.stderr +++ b/tests/ui/implicit_hasher.stderr @@ -1,40 +1,121 @@ -error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` - --> tests/ui/implicit_hasher.rs:14:1 +error: impl for `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:15:35 + | +LL | impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> { + | ^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/implicit_hasher.rs:2:9 + | +LL | #![deny(clippy::implicit_hasher)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: add a type parameter for `BuildHasher` + | +LL ~ impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> { +LL | fn make() -> (Self, Self) { +... +LL | +LL ~ (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + | + +error: impl for `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:24:36 + | +LL | impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) { + | ^^^^^^^^^^^^^ + | +help: add a type parameter for `BuildHasher` + | +LL ~ impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) { +LL | fn make() -> (Self, Self) { +LL ~ ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),)) + | + +error: impl for `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:29:19 + | +LL | impl Foo<i16> for HashMap<String, String> { + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add a type parameter for `BuildHasher` + | +LL ~ impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> { +LL | fn make() -> (Self, Self) { +LL ~ (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + | + +error: impl for `HashSet` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:46:32 + | +LL | impl<T: Hash + Eq> Foo<i8> for HashSet<T> { + | ^^^^^^^^^^ + | +help: add a type parameter for `BuildHasher` + | +LL ~ impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> { +LL | fn make() -> (Self, Self) { +LL ~ (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default())) | -LL | pub trait Foo<T>: Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^ -error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` - --> tests/ui/implicit_hasher.rs:71:1 +error: impl for `HashSet` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:51:19 + | +LL | impl Foo<i16> for HashSet<String> { + | ^^^^^^^^^^^^^^^ + | +help: add a type parameter for `BuildHasher` + | +LL ~ impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> { +LL | fn make() -> (Self, Self) { +LL ~ (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default())) | -LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` - --> tests/ui/implicit_hasher.rs:74:1 +error: parameter of type `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:68:22 + | +LL | pub fn map(map: &mut HashMap<i32, i32>) {} + | ^^^^^^^^^^^^^^^^^ | -LL | pub mod gen { - | ^^^^^^^^^^^ +help: add a type parameter for `BuildHasher` + | +LL | pub fn map<S: ::std::hash::BuildHasher>(map: &mut HashMap<i32, i32, S>) {} + | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~ -error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` - --> tests/ui/implicit_hasher.rs:92:1 +error: parameter of type `HashSet` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:70:22 + | +LL | pub fn set(set: &mut HashSet<i32>) {} + | ^^^^^^^^^^^^ | -LL | pub mod test_macro; - | ^^^^^^^^^^^^^^^^^^^ +help: add a type parameter for `BuildHasher` + | +LL | pub fn set<S: ::std::hash::BuildHasher>(set: &mut HashSet<i32, S>) {} + | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~ -error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` - --> tests/ui/implicit_hasher.rs:96:1 +error: impl for `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:76:43 + | +LL | impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> { + | ^^^^^^^^^^^^^ | -LL | external! { - | ^^^^^^^^^ + = note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add a type parameter for `BuildHasher` + | +LL ~ impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> { +LL | fn make() -> (Self, Self) { +LL ~ (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) | - = note: this error originates in the macro `external` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` - --> tests/ui/implicit_hasher.rs:101:1 +error: parameter of type `HashMap` should be generalized over different hashers + --> tests/ui/implicit_hasher.rs:100:35 | LL | pub async fn election_vote(_data: HashMap<i32, i32>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ + | +help: add a type parameter for `BuildHasher` + | +LL | pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {} + | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 6 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/implicit_return.stderr b/tests/ui/implicit_return.stderr index f06d4e983c57a..3b06f26f5a05f 100644 --- a/tests/ui/implicit_return.stderr +++ b/tests/ui/implicit_return.stderr @@ -2,88 +2,157 @@ error: missing `return` statement --> tests/ui/implicit_return.rs:15:5 | LL | true - | ^^^^ help: add `return` as shown: `return true` + | ^^^^ | = note: `-D clippy::implicit-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::implicit_return)]` +help: add `return` as shown + | +LL | return true + | error: missing `return` statement --> tests/ui/implicit_return.rs:19:15 | LL | if true { true } else { false } - | ^^^^ help: add `return` as shown: `return true` + | ^^^^ + | +help: add `return` as shown + | +LL | if true { return true } else { false } + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:19:29 | LL | if true { true } else { false } - | ^^^^^ help: add `return` as shown: `return false` + | ^^^^^ + | +help: add `return` as shown + | +LL | if true { true } else { return false } + | ~~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:25:17 | LL | true => false, - | ^^^^^ help: add `return` as shown: `return false` + | ^^^^^ + | +help: add `return` as shown + | +LL | true => return false, + | ~~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:26:20 | LL | false => { true }, - | ^^^^ help: add `return` as shown: `return true` + | ^^^^ + | +help: add `return` as shown + | +LL | false => { return true }, + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:39:9 | LL | break true; - | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + | ^^^^^^^^^^ + | +help: change `break` to `return` as shown + | +LL | return true; + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:46:13 | LL | break true; - | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + | ^^^^^^^^^^ + | +help: change `break` to `return` as shown + | +LL | return true; + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:54:13 | LL | break true; - | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + | ^^^^^^^^^^ + | +help: change `break` to `return` as shown + | +LL | return true; + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:72:18 | LL | let _ = || { true }; - | ^^^^ help: add `return` as shown: `return true` + | ^^^^ + | +help: add `return` as shown + | +LL | let _ = || { return true }; + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:73:16 | LL | let _ = || true; - | ^^^^ help: add `return` as shown: `return true` + | ^^^^ + | +help: add `return` as shown + | +LL | let _ = || return true; + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:81:5 | LL | format!("test {}", "test") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add `return` as shown + | +LL | return format!("test {}", "test") + | error: missing `return` statement --> tests/ui/implicit_return.rs:90:5 | LL | m!(true, false) - | ^^^^^^^^^^^^^^^ help: add `return` as shown: `return m!(true, false)` + | ^^^^^^^^^^^^^^^ + | +help: add `return` as shown + | +LL | return m!(true, false) + | error: missing `return` statement --> tests/ui/implicit_return.rs:96:13 | LL | break true; - | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + | ^^^^^^^^^^ + | +help: change `break` to `return` as shown + | +LL | return true; + | ~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:101:17 | LL | break 'outer false; - | ^^^^^^^^^^^^^^^^^^ help: change `break` to `return` as shown: `return false` + | ^^^^^^^^^^^^^^^^^^ + | +help: change `break` to `return` as shown + | +LL | return false; + | ~~~~~~~~~~~~ error: missing `return` statement --> tests/ui/implicit_return.rs:116:5 @@ -104,7 +173,12 @@ error: missing `return` statement --> tests/ui/implicit_return.rs:130:5 | LL | true - | ^^^^ help: add `return` as shown: `return true` + | ^^^^ + | +help: add `return` as shown + | +LL | return true + | error: aborting due to 16 previous errors diff --git a/tests/ui/lossy_float_literal.stderr b/tests/ui/lossy_float_literal.stderr index b5a07418734c9..3026854e317ae 100644 --- a/tests/ui/lossy_float_literal.stderr +++ b/tests/ui/lossy_float_literal.stderr @@ -2,70 +2,124 @@ error: literal cannot be represented as the underlying type without loss of prec --> tests/ui/lossy_float_literal.rs:14:18 | LL | let _: f32 = 16_777_217.0; - | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_216.0` + | ^^^^^^^^^^^^ | = note: `-D clippy::lossy-float-literal` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::lossy_float_literal)]` +help: consider changing the type or replacing it with + | +LL | let _: f32 = 16_777_216.0; + | ~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:15:18 | LL | let _: f32 = 16_777_219.0; - | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + | ^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f32 = 16_777_220.0; + | ~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:16:18 | LL | let _: f32 = 16_777_219.; - | ^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + | ^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f32 = 16_777_220.0; + | ~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:17:18 | LL | let _: f32 = 16_777_219.000; - | ^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + | ^^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f32 = 16_777_220.0; + | ~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:18:13 | LL | let _ = 16_777_219f32; - | ^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220_f32` + | ^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _ = 16_777_220_f32; + | ~~~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:19:19 | LL | let _: f32 = -16_777_219.0; - | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + | ^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f32 = -16_777_220.0; + | ~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:21:18 | LL | let _: f64 = 9_007_199_254_740_993.0; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f64 = 9_007_199_254_740_992.0; + | ~~~~~~~~~~~~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:22:18 | LL | let _: f64 = 9_007_199_254_740_993.; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f64 = 9_007_199_254_740_992.0; + | ~~~~~~~~~~~~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:23:18 | LL | let _: f64 = 9_007_199_254_740_993.00; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f64 = 9_007_199_254_740_992.0; + | ~~~~~~~~~~~~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:24:13 | LL | let _ = 9_007_199_254_740_993f64; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992_f64` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _ = 9_007_199_254_740_992_f64; + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error: literal cannot be represented as the underlying type without loss of precision --> tests/ui/lossy_float_literal.rs:25:19 | LL | let _: f64 = -9_007_199_254_740_993.0; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the type or replacing it with + | +LL | let _: f64 = -9_007_199_254_740_992.0; + | ~~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 11 previous errors diff --git a/tests/ui/missing_trait_methods.rs b/tests/ui/missing_trait_methods.rs index 1b09b717ff70a..55b904bde59af 100644 --- a/tests/ui/missing_trait_methods.rs +++ b/tests/ui/missing_trait_methods.rs @@ -49,4 +49,12 @@ impl B for Complete { } } +trait MissingMultiple { + fn one() {} + fn two() {} + fn three() {} +} + +impl MissingMultiple for Partial {} + fn main() {} diff --git a/tests/ui/missing_trait_methods.stderr b/tests/ui/missing_trait_methods.stderr index f5d5d4418b287..9c4968b022d7e 100644 --- a/tests/ui/missing_trait_methods.stderr +++ b/tests/ui/missing_trait_methods.stderr @@ -24,5 +24,41 @@ help: implement the method LL | fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: missing trait method provided by default: `one` + --> tests/ui/missing_trait_methods.rs:58:1 + | +LL | impl MissingMultiple for Partial {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> tests/ui/missing_trait_methods.rs:53:5 + | +LL | fn one() {} + | ^^^^^^^^ + +error: missing trait method provided by default: `two` + --> tests/ui/missing_trait_methods.rs:58:1 + | +LL | impl MissingMultiple for Partial {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> tests/ui/missing_trait_methods.rs:54:5 + | +LL | fn two() {} + | ^^^^^^^^ + +error: missing trait method provided by default: `three` + --> tests/ui/missing_trait_methods.rs:58:1 + | +LL | impl MissingMultiple for Partial {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> tests/ui/missing_trait_methods.rs:55:5 + | +LL | fn three() {} + | ^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/needless_borrows_for_generic_args.fixed b/tests/ui/needless_borrows_for_generic_args.fixed index 5478372cbe00f..c1dc8b5e8d09c 100644 --- a/tests/ui/needless_borrows_for_generic_args.fixed +++ b/tests/ui/needless_borrows_for_generic_args.fixed @@ -333,4 +333,12 @@ fn main() { f(&y); // Don't lint f("".to_owned()); // Lint } + { + fn takes_writer<T: std::io::Write>(_: T) {} + + fn issue_12856(mut buffer: &mut Vec<u8>) { + takes_writer(&mut buffer); // Don't lint, would make buffer unavailable later + buffer.extend(b"\n"); + } + } } diff --git a/tests/ui/needless_borrows_for_generic_args.rs b/tests/ui/needless_borrows_for_generic_args.rs index 2643815d939b5..c7f66824d5818 100644 --- a/tests/ui/needless_borrows_for_generic_args.rs +++ b/tests/ui/needless_borrows_for_generic_args.rs @@ -333,4 +333,12 @@ fn main() { f(&y); // Don't lint f(&"".to_owned()); // Lint } + { + fn takes_writer<T: std::io::Write>(_: T) {} + + fn issue_12856(mut buffer: &mut Vec<u8>) { + takes_writer(&mut buffer); // Don't lint, would make buffer unavailable later + buffer.extend(b"\n"); + } + } } diff --git a/tests/ui/needless_pub_self.stderr b/tests/ui/needless_pub_self.stderr index 0bff2e4b8b71b..1fdd841656594 100644 --- a/tests/ui/needless_pub_self.stderr +++ b/tests/ui/needless_pub_self.stderr @@ -2,22 +2,27 @@ error: unnecessary `pub(self)` --> tests/ui/needless_pub_self.rs:13:1 | LL | pub(self) fn a() {} - | ^^^^^^^^^ help: remove it + | ^^^^^^^^^ | = note: `-D clippy::needless-pub-self` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_pub_self)]` + = help: remove it error: unnecessary `pub(in self)` --> tests/ui/needless_pub_self.rs:14:1 | LL | pub(in self) fn b() {} - | ^^^^^^^^^^^^ help: remove it + | ^^^^^^^^^^^^ + | + = help: remove it error: unnecessary `pub(self)` --> tests/ui/needless_pub_self.rs:20:5 | LL | pub(self) fn f() {} - | ^^^^^^^^^ help: remove it + | ^^^^^^^^^ + | + = help: remove it error: aborting due to 3 previous errors diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index d117e8bf9c7a1..52b0155a762ea 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -177,3 +177,9 @@ fn issue_12371(x: usize) -> bool { // Should not warn! !x != 0 } + +// Not linted because it is slow to do so +// https://github.com/rust-lang/rust-clippy/issues/13206 +fn many_ops(a: bool, b: bool, c: bool, d: bool, e: bool, f: bool) -> bool { + (a && c && f) || (!a && b && !d) || (!b && !c && !e) || (d && e && !f) +} diff --git a/tests/ui/patterns.fixed b/tests/ui/patterns.fixed index 332cba9715575..feaec33ac15a1 100644 --- a/tests/ui/patterns.fixed +++ b/tests/ui/patterns.fixed @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::all)] #![allow(unused)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::single_match)] #[macro_use] extern crate proc_macros; diff --git a/tests/ui/patterns.rs b/tests/ui/patterns.rs index 45d907688e379..53812c7deec3e 100644 --- a/tests/ui/patterns.rs +++ b/tests/ui/patterns.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::all)] #![allow(unused)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::single_match)] #[macro_use] extern crate proc_macros; diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index d70c9f8d06c07..b810fd8224f63 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -13,7 +13,10 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] +#![allow(clippy::manual_find_map)] +#![allow(clippy::manual_filter_map)] #![allow(clippy::useless_conversion)] +#![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] #![allow(clippy::non_canonical_clone_impl)] #![allow(clippy::non_canonical_partial_ord_impl)] @@ -47,6 +50,7 @@ #![allow(invalid_value)] #![allow(invalid_from_utf8_unchecked)] #![allow(let_underscore_drop)] +#![allow(unexpected_cfgs)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] @@ -55,65 +59,72 @@ #![allow(unknown_lints)] #![allow(unused_labels)] #![allow(ambiguous_wide_pointer_comparisons)] -#![warn(clippy::almost_complete_range)] -#![warn(clippy::disallowed_names)] -#![warn(clippy::blocks_in_conditions)] -#![warn(clippy::blocks_in_conditions)] -#![warn(clippy::blocks_in_conditions)] -#![warn(clippy::box_collection)] -#![warn(clippy::redundant_static_lifetimes)] -#![warn(clippy::cognitive_complexity)] -#![warn(clippy::derived_hash_with_manual_eq)] -#![warn(clippy::disallowed_methods)] -#![warn(clippy::disallowed_types)] -#![warn(clippy::mixed_read_write_in_expression)] -#![warn(clippy::useless_conversion)] -#![warn(clippy::match_result_ok)] -#![warn(clippy::non_canonical_clone_impl)] -#![warn(clippy::non_canonical_partial_ord_impl)] -#![warn(clippy::arithmetic_side_effects)] -#![warn(clippy::overly_complex_bool_expr)] -#![warn(clippy::new_without_default)] -#![warn(clippy::bind_instead_of_map)] -#![warn(clippy::expect_used)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::unwrap_used)] -#![warn(clippy::panicking_overflow_checks)] -#![warn(clippy::needless_borrow)] -#![warn(clippy::expect_used)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::unwrap_used)] -#![warn(clippy::single_char_add_str)] -#![warn(clippy::module_name_repetitions)] -#![warn(clippy::missing_const_for_thread_local)] -#![warn(clippy::recursive_format_impl)] -#![warn(clippy::unwrap_or_default)] -#![warn(clippy::invisible_characters)] -#![warn(invalid_reference_casting)] -#![warn(suspicious_double_ref_op)] -#![warn(invalid_nan_comparisons)] -#![warn(drop_bounds)] -#![warn(dropping_copy_types)] -#![warn(dropping_references)] -#![warn(useless_ptr_null_checks)] -#![warn(for_loops_over_fallibles)] -#![warn(for_loops_over_fallibles)] -#![warn(for_loops_over_fallibles)] -#![warn(forgetting_copy_types)] -#![warn(forgetting_references)] -#![warn(array_into_iter)] -#![warn(invalid_atomic_ordering)] -#![warn(invalid_value)] -#![warn(invalid_from_utf8_unchecked)] -#![warn(let_underscore_drop)] -#![warn(enum_intrinsics_non_enums)] -#![warn(non_fmt_panics)] -#![warn(named_arguments_used_positionally)] -#![warn(temporary_cstring_as_ptr)] -#![warn(undropped_manually_drops)] -#![warn(unknown_lints)] -#![warn(unused_labels)] -#![warn(ambiguous_wide_pointer_comparisons)] +#![allow(clippy::reversed_empty_ranges)] +#![warn(clippy::almost_complete_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` +#![warn(clippy::disallowed_names)] //~ ERROR: lint `clippy::blacklisted_name` +#![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_expr` +#![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_stmt` +#![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions` +#![warn(clippy::box_collection)] //~ ERROR: lint `clippy::box_vec` +#![warn(clippy::redundant_static_lifetimes)] //~ ERROR: lint `clippy::const_static_lifetime` +#![warn(clippy::cognitive_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity` +#![warn(clippy::derived_hash_with_manual_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq` +#![warn(clippy::disallowed_methods)] //~ ERROR: lint `clippy::disallowed_method` +#![warn(clippy::disallowed_types)] //~ ERROR: lint `clippy::disallowed_type` +#![warn(clippy::mixed_read_write_in_expression)] //~ ERROR: lint `clippy::eval_order_dependence` +#![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` +#![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map` +#![warn(clippy::useless_conversion)] //~ ERROR: lint `clippy::identity_conversion` +#![warn(clippy::redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` +#![warn(clippy::match_result_ok)] //~ ERROR: lint `clippy::if_let_some_result` +#![warn(clippy::non_canonical_clone_impl)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type` +#![warn(clippy::non_canonical_partial_ord_impl)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type` +#![warn(clippy::arithmetic_side_effects)] //~ ERROR: lint `clippy::integer_arithmetic` +#![warn(clippy::overly_complex_bool_expr)] //~ ERROR: lint `clippy::logic_bug` +#![warn(clippy::new_without_default)] //~ ERROR: lint `clippy::new_without_default_derive` +#![warn(clippy::bind_instead_of_map)] //~ ERROR: lint `clippy::option_and_then_some` +#![warn(clippy::expect_used)] //~ ERROR: lint `clippy::option_expect_used` +#![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or` +#![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or_else` +#![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used` +#![warn(clippy::panicking_overflow_checks)] //~ ERROR: lint `clippy::overflow_check_conditional` +#![warn(clippy::needless_borrow)] //~ ERROR: lint `clippy::ref_in_deref` +#![warn(clippy::expect_used)] //~ ERROR: lint `clippy::result_expect_used` +#![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` +#![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` +#![warn(clippy::single_char_add_str)] //~ ERROR: lint `clippy::single_char_push_str` +#![warn(clippy::module_name_repetitions)] //~ ERROR: lint `clippy::stutter` +#![warn(clippy::missing_const_for_thread_local)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` +#![warn(clippy::recursive_format_impl)] //~ ERROR: lint `clippy::to_string_in_display` +#![warn(clippy::unwrap_or_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` +#![warn(clippy::invisible_characters)] //~ ERROR: lint `clippy::zero_width_space` +#![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut` +#![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref` +#![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan` +#![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` +#![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` +#![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` +#![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result` +#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` +#![warn(forgetting_copy_types)] //~ ERROR: lint `clippy::forget_copy` +#![warn(forgetting_references)] //~ ERROR: lint `clippy::forget_ref` +#![warn(array_into_iter)] //~ ERROR: lint `clippy::into_iter_on_array` +#![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` +#![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref` +#![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` +#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg` +#![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` +#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os` +#![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params` +#![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters` +#![warn(temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` +#![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` +#![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` +#![warn(ambiguous_wide_pointer_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` +#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` fn main() {} diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 8d0ac3c8f9555..e03df1658ee36 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -13,7 +13,10 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] +#![allow(clippy::manual_find_map)] +#![allow(clippy::manual_filter_map)] #![allow(clippy::useless_conversion)] +#![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] #![allow(clippy::non_canonical_clone_impl)] #![allow(clippy::non_canonical_partial_ord_impl)] @@ -47,6 +50,7 @@ #![allow(invalid_value)] #![allow(invalid_from_utf8_unchecked)] #![allow(let_underscore_drop)] +#![allow(unexpected_cfgs)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] @@ -55,65 +59,72 @@ #![allow(unknown_lints)] #![allow(unused_labels)] #![allow(ambiguous_wide_pointer_comparisons)] -#![warn(clippy::almost_complete_letter_range)] -#![warn(clippy::blacklisted_name)] -#![warn(clippy::block_in_if_condition_expr)] -#![warn(clippy::block_in_if_condition_stmt)] -#![warn(clippy::blocks_in_if_conditions)] -#![warn(clippy::box_vec)] -#![warn(clippy::const_static_lifetime)] -#![warn(clippy::cyclomatic_complexity)] -#![warn(clippy::derive_hash_xor_eq)] -#![warn(clippy::disallowed_method)] -#![warn(clippy::disallowed_type)] -#![warn(clippy::eval_order_dependence)] -#![warn(clippy::identity_conversion)] -#![warn(clippy::if_let_some_result)] -#![warn(clippy::incorrect_clone_impl_on_copy_type)] -#![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] -#![warn(clippy::integer_arithmetic)] -#![warn(clippy::logic_bug)] -#![warn(clippy::new_without_default_derive)] -#![warn(clippy::option_and_then_some)] -#![warn(clippy::option_expect_used)] -#![warn(clippy::option_map_unwrap_or)] -#![warn(clippy::option_map_unwrap_or_else)] -#![warn(clippy::option_unwrap_used)] -#![warn(clippy::overflow_check_conditional)] -#![warn(clippy::ref_in_deref)] -#![warn(clippy::result_expect_used)] -#![warn(clippy::result_map_unwrap_or_else)] -#![warn(clippy::result_unwrap_used)] -#![warn(clippy::single_char_push_str)] -#![warn(clippy::stutter)] -#![warn(clippy::thread_local_initializer_can_be_made_const)] -#![warn(clippy::to_string_in_display)] -#![warn(clippy::unwrap_or_else_default)] -#![warn(clippy::zero_width_space)] -#![warn(clippy::cast_ref_to_mut)] -#![warn(clippy::clone_double_ref)] -#![warn(clippy::cmp_nan)] -#![warn(clippy::drop_bounds)] -#![warn(clippy::drop_copy)] -#![warn(clippy::drop_ref)] -#![warn(clippy::fn_null_check)] -#![warn(clippy::for_loop_over_option)] -#![warn(clippy::for_loop_over_result)] -#![warn(clippy::for_loops_over_fallibles)] -#![warn(clippy::forget_copy)] -#![warn(clippy::forget_ref)] -#![warn(clippy::into_iter_on_array)] -#![warn(clippy::invalid_atomic_ordering)] -#![warn(clippy::invalid_ref)] -#![warn(clippy::invalid_utf8_in_unchecked)] -#![warn(clippy::let_underscore_drop)] -#![warn(clippy::mem_discriminant_non_enum)] -#![warn(clippy::panic_params)] -#![warn(clippy::positional_named_format_parameters)] -#![warn(clippy::temporary_cstring_as_ptr)] -#![warn(clippy::undropped_manually_drops)] -#![warn(clippy::unknown_clippy_lints)] -#![warn(clippy::unused_label)] -#![warn(clippy::vtable_address_comparisons)] +#![allow(clippy::reversed_empty_ranges)] +#![warn(clippy::almost_complete_letter_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` +#![warn(clippy::blacklisted_name)] //~ ERROR: lint `clippy::blacklisted_name` +#![warn(clippy::block_in_if_condition_expr)] //~ ERROR: lint `clippy::block_in_if_condition_expr` +#![warn(clippy::block_in_if_condition_stmt)] //~ ERROR: lint `clippy::block_in_if_condition_stmt` +#![warn(clippy::blocks_in_if_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions` +#![warn(clippy::box_vec)] //~ ERROR: lint `clippy::box_vec` +#![warn(clippy::const_static_lifetime)] //~ ERROR: lint `clippy::const_static_lifetime` +#![warn(clippy::cyclomatic_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity` +#![warn(clippy::derive_hash_xor_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq` +#![warn(clippy::disallowed_method)] //~ ERROR: lint `clippy::disallowed_method` +#![warn(clippy::disallowed_type)] //~ ERROR: lint `clippy::disallowed_type` +#![warn(clippy::eval_order_dependence)] //~ ERROR: lint `clippy::eval_order_dependence` +#![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` +#![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map` +#![warn(clippy::identity_conversion)] //~ ERROR: lint `clippy::identity_conversion` +#![warn(clippy::if_let_redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` +#![warn(clippy::if_let_some_result)] //~ ERROR: lint `clippy::if_let_some_result` +#![warn(clippy::incorrect_clone_impl_on_copy_type)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type` +#![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type` +#![warn(clippy::integer_arithmetic)] //~ ERROR: lint `clippy::integer_arithmetic` +#![warn(clippy::logic_bug)] //~ ERROR: lint `clippy::logic_bug` +#![warn(clippy::new_without_default_derive)] //~ ERROR: lint `clippy::new_without_default_derive` +#![warn(clippy::option_and_then_some)] //~ ERROR: lint `clippy::option_and_then_some` +#![warn(clippy::option_expect_used)] //~ ERROR: lint `clippy::option_expect_used` +#![warn(clippy::option_map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or` +#![warn(clippy::option_map_unwrap_or_else)] //~ ERROR: lint `clippy::option_map_unwrap_or_else` +#![warn(clippy::option_unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used` +#![warn(clippy::overflow_check_conditional)] //~ ERROR: lint `clippy::overflow_check_conditional` +#![warn(clippy::ref_in_deref)] //~ ERROR: lint `clippy::ref_in_deref` +#![warn(clippy::result_expect_used)] //~ ERROR: lint `clippy::result_expect_used` +#![warn(clippy::result_map_unwrap_or_else)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` +#![warn(clippy::result_unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` +#![warn(clippy::single_char_push_str)] //~ ERROR: lint `clippy::single_char_push_str` +#![warn(clippy::stutter)] //~ ERROR: lint `clippy::stutter` +#![warn(clippy::thread_local_initializer_can_be_made_const)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` +#![warn(clippy::to_string_in_display)] //~ ERROR: lint `clippy::to_string_in_display` +#![warn(clippy::unwrap_or_else_default)] //~ ERROR: lint `clippy::unwrap_or_else_default` +#![warn(clippy::zero_width_space)] //~ ERROR: lint `clippy::zero_width_space` +#![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut` +#![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref` +#![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan` +#![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` +#![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` +#![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` +#![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check` +#![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option` +#![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result` +#![warn(clippy::for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles` +#![warn(clippy::forget_copy)] //~ ERROR: lint `clippy::forget_copy` +#![warn(clippy::forget_ref)] //~ ERROR: lint `clippy::forget_ref` +#![warn(clippy::into_iter_on_array)] //~ ERROR: lint `clippy::into_iter_on_array` +#![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering` +#![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref` +#![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked` +#![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop` +#![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg` +#![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum` +#![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os` +#![warn(clippy::panic_params)] //~ ERROR: lint `clippy::panic_params` +#![warn(clippy::positional_named_format_parameters)] //~ ERROR: lint `clippy::positional_named_format_parameters` +#![warn(clippy::temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr` +#![warn(clippy::undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` +#![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` +#![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` +#![warn(clippy::vtable_address_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` +#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` fn main() {} diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index d6637324a030b..46d9f0fac59f9 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:58:9 + --> tests/ui/rename.rs:63:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,358 +8,394 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:59:9 + --> tests/ui/rename.rs:64:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:60:9 + --> tests/ui/rename.rs:65:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:61:9 + --> tests/ui/rename.rs:66:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:62:9 + --> tests/ui/rename.rs:67:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:63:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:64:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:65:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:66:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` +error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` + --> tests/ui/rename.rs:75:9 + | +LL | #![warn(clippy::find_map)] + | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` + +error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` + --> tests/ui/rename.rs:76:9 + | +LL | #![warn(clippy::filter_map)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` + error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` +error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` + --> tests/ui/rename.rs:78:9 + | +LL | #![warn(clippy::if_let_redundant_pattern_matching)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` + error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:99:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:100:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:99:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:100:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:112:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:114:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:115:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:116:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:117:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` +error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` + --> tests/ui/rename.rs:118:9 + | +LL | #![warn(clippy::maybe_misused_cfg)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` + error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:119:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` +error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` + --> tests/ui/rename.rs:120:9 + | +LL | #![warn(clippy::mismatched_target_os)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` + error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:121:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:122:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> tests/ui/rename.rs:113:9 + --> tests/ui/rename.rs:123:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:114:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:115:9 + --> tests/ui/rename.rs:125:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:116:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:117:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` -error: aborting due to 60 previous errors +error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` + --> tests/ui/rename.rs:128:9 + | +LL | #![warn(clippy::reverse_range_loop)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` + +error: aborting due to 66 previous errors diff --git a/tests/ui/set_contains_or_insert.rs b/tests/ui/set_contains_or_insert.rs index 8465007402ab0..d3a3e1c878b3a 100644 --- a/tests/ui/set_contains_or_insert.rs +++ b/tests/ui/set_contains_or_insert.rs @@ -3,17 +3,78 @@ #![allow(clippy::needless_borrow)] #![warn(clippy::set_contains_or_insert)] -use std::collections::HashSet; +use std::collections::{BTreeSet, HashSet}; -fn main() { - should_warn_cases(); +fn should_warn_hashset() { + let mut set = HashSet::new(); + let value = 5; + + if !set.contains(&value) { + set.insert(value); + println!("Just a comment"); + } + + if set.contains(&value) { + set.insert(value); + println!("Just a comment"); + } + + if !set.contains(&value) { + set.insert(value); + } - should_not_warn_cases(); + if !!set.contains(&value) { + set.insert(value); + println!("Just a comment"); + } + + if (&set).contains(&value) { + set.insert(value); + } + + let borrow_value = &6; + if !set.contains(borrow_value) { + set.insert(*borrow_value); + } + + let borrow_set = &mut set; + if !borrow_set.contains(&value) { + borrow_set.insert(value); + } } -fn should_warn_cases() { +fn should_not_warn_hashset() { let mut set = HashSet::new(); let value = 5; + let another_value = 6; + + if !set.contains(&value) { + set.insert(another_value); + } + + if !set.contains(&value) { + println!("Just a comment"); + } + + if simply_true() { + set.insert(value); + } + + if !set.contains(&value) { + set.replace(value); //it is not insert + println!("Just a comment"); + } + + if set.contains(&value) { + println!("value is already in set"); + } else { + set.insert(value); + } +} + +fn should_warn_btreeset() { + let mut set = BTreeSet::new(); + let value = 5; if !set.contains(&value) { set.insert(value); @@ -49,8 +110,8 @@ fn should_warn_cases() { } } -fn should_not_warn_cases() { - let mut set = HashSet::new(); +fn should_not_warn_btreeset() { + let mut set = BTreeSet::new(); let value = 5; let another_value = 6; @@ -81,3 +142,11 @@ fn should_not_warn_cases() { fn simply_true() -> bool { true } + +// This is placed last in order to be able to add new tests without changing line numbers +fn main() { + should_warn_hashset(); + should_warn_btreeset(); + should_not_warn_hashset(); + should_not_warn_btreeset(); +} diff --git a/tests/ui/set_contains_or_insert.stderr b/tests/ui/set_contains_or_insert.stderr index 507e20964fc20..14ad630054482 100644 --- a/tests/ui/set_contains_or_insert.stderr +++ b/tests/ui/set_contains_or_insert.stderr @@ -1,5 +1,5 @@ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:18:13 + --> tests/ui/set_contains_or_insert.rs:12:13 | LL | if !set.contains(&value) { | ^^^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | set.insert(value); = help: to override `-D warnings` add `#[allow(clippy::set_contains_or_insert)]` error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:23:12 + --> tests/ui/set_contains_or_insert.rs:17:12 | LL | if set.contains(&value) { | ^^^^^^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | set.insert(value); | ^^^^^^^^^^^^^ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:28:13 + --> tests/ui/set_contains_or_insert.rs:22:13 | LL | if !set.contains(&value) { | ^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | set.insert(value); | ^^^^^^^^^^^^^ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:32:14 + --> tests/ui/set_contains_or_insert.rs:26:14 | LL | if !!set.contains(&value) { | ^^^^^^^^^^^^^^^^ @@ -34,7 +34,7 @@ LL | set.insert(value); | ^^^^^^^^^^^^^ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:37:15 + --> tests/ui/set_contains_or_insert.rs:31:15 | LL | if (&set).contains(&value) { | ^^^^^^^^^^^^^^^^ @@ -42,7 +42,7 @@ LL | set.insert(value); | ^^^^^^^^^^^^^ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:42:13 + --> tests/ui/set_contains_or_insert.rs:36:13 | LL | if !set.contains(borrow_value) { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -50,12 +50,68 @@ LL | set.insert(*borrow_value); | ^^^^^^^^^^^^^^^^^^^^^ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:47:20 + --> tests/ui/set_contains_or_insert.rs:41:20 | LL | if !borrow_set.contains(&value) { | ^^^^^^^^^^^^^^^^ LL | borrow_set.insert(value); | ^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:79:13 + | +LL | if !set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:84:12 + | +LL | if set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:89:13 + | +LL | if !set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:93:14 + | +LL | if !!set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:98:15 + | +LL | if (&set).contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | set.insert(value); + | ^^^^^^^^^^^^^ + +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:103:13 + | +LL | if !set.contains(borrow_value) { + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | set.insert(*borrow_value); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: usage of `BTreeSet::insert` after `BTreeSet::contains` + --> tests/ui/set_contains_or_insert.rs:108:20 + | +LL | if !borrow_set.contains(&value) { + | ^^^^^^^^^^^^^^^^ +LL | borrow_set.insert(value); + | ^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index acd70416d8bf0..5249c4408899a 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -253,3 +253,46 @@ mod issue8634 { } } } + +fn issue11365() { + enum Foo { + A, + B, + C, + } + use Foo::{A, B, C}; + + match Some(A) { + Some(A | B | C) => println!(), + None => {}, + } + + match Some(A) { + Some(A | B) => println!(), + Some { 0: C } | None => {}, + } + + match [A, A] { + [A, _] => println!(), + [_, A | B | C] => {}, + } + + match Ok::<_, u32>(Some(A)) { + Ok(Some(A)) => println!(), + Err(_) | Ok(None | Some(B | C)) => {}, + } + + if let Ok(Some(A)) = Ok::<_, u32>(Some(A)) { println!() } + + match &Some(A) { + Some(A | B | C) => println!(), + None => {}, + } + + match &Some(A) { + &Some(A | B | C) => println!(), + None => {}, + } + + if let Some(A | B) = &Some(A) { println!() } +} diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index bde7819981089..882098a56e780 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -311,3 +311,52 @@ mod issue8634 { } } } + +fn issue11365() { + enum Foo { + A, + B, + C, + } + use Foo::{A, B, C}; + + match Some(A) { + Some(A | B | C) => println!(), + None => {}, + } + + match Some(A) { + Some(A | B) => println!(), + Some { 0: C } | None => {}, + } + + match [A, A] { + [A, _] => println!(), + [_, A | B | C] => {}, + } + + match Ok::<_, u32>(Some(A)) { + Ok(Some(A)) => println!(), + Err(_) | Ok(None | Some(B | C)) => {}, + } + + match Ok::<_, u32>(Some(A)) { + Ok(Some(A)) => println!(), + Err(_) | Ok(None | Some(_)) => {}, + } + + match &Some(A) { + Some(A | B | C) => println!(), + None => {}, + } + + match &Some(A) { + &Some(A | B | C) => println!(), + None => {}, + } + + match &Some(A) { + Some(A | B) => println!(), + None | Some(_) => {}, + } +} diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index a249c120ee4a4..ceb2a193bf7b1 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -198,5 +198,23 @@ LL + } LL + } | -error: aborting due to 18 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:343:5 + | +LL | / match Ok::<_, u32>(Some(A)) { +LL | | Ok(Some(A)) => println!(), +LL | | Err(_) | Ok(None | Some(_)) => {}, +LL | | } + | |_____^ help: try: `if let Ok(Some(A)) = Ok::<_, u32>(Some(A)) { println!() }` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> tests/ui/single_match.rs:358:5 + | +LL | / match &Some(A) { +LL | | Some(A | B) => println!(), +LL | | None | Some(_) => {}, +LL | | } + | |_____^ help: try: `if let Some(A | B) = &Some(A) { println!() }` + +error: aborting due to 20 previous errors diff --git a/tests/ui/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed index 6ede7bfcd9f66..227b98c683e97 100644 --- a/tests/ui/std_instead_of_core.fixed +++ b/tests/ui/std_instead_of_core.fixed @@ -75,8 +75,17 @@ mod std_in_proc_macro_derive { struct B {} } -fn main() { - std_instead_of_core(); - std_instead_of_alloc(); - alloc_instead_of_core(); +// Some intrinsics are usable on stable but live in an unstable module, but should still suggest +// replacing std -> core +fn intrinsic(a: *mut u8, b: *mut u8) { + unsafe { + core::intrinsics::copy(a, b, 1); + //~^ std_instead_of_core + } } + +#[clippy::msrv = "1.76"] +fn msrv_1_76(_: std::net::IpAddr) {} + +#[clippy::msrv = "1.77"] +fn msrv_1_77(_: core::net::IpAddr) {} diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index e22b4f61f3ecc..01bb78dd3bf1d 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -75,8 +75,17 @@ mod std_in_proc_macro_derive { struct B {} } -fn main() { - std_instead_of_core(); - std_instead_of_alloc(); - alloc_instead_of_core(); +// Some intrinsics are usable on stable but live in an unstable module, but should still suggest +// replacing std -> core +fn intrinsic(a: *mut u8, b: *mut u8) { + unsafe { + std::intrinsics::copy(a, b, 1); + //~^ std_instead_of_core + } } + +#[clippy::msrv = "1.76"] +fn msrv_1_76(_: std::net::IpAddr) {} + +#[clippy::msrv = "1.77"] +fn msrv_1_77(_: std::net::IpAddr) {} diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index 22cb9db7050b8..45d60d235ceb3 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr @@ -85,5 +85,17 @@ LL | use alloc::slice::from_ref; = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` -error: aborting due to 13 previous errors +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:82:9 + | +LL | std::intrinsics::copy(a, b, 1); + | ^^^ help: consider importing the item from `core`: `core` + +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:91:17 + | +LL | fn msrv_1_77(_: std::net::IpAddr) {} + | ^^^ help: consider importing the item from `core`: `core` + +error: aborting due to 15 previous errors diff --git a/tests/ui/suspicious_xor_used_as_pow.stderr b/tests/ui/suspicious_xor_used_as_pow.stderr index 4faf0237c17ad..43b03676b1db5 100644 --- a/tests/ui/suspicious_xor_used_as_pow.stderr +++ b/tests/ui/suspicious_xor_used_as_pow.stderr @@ -2,51 +2,84 @@ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:19:13 | LL | let _ = 2 ^ 5; - | ^^^^^ help: did you mean to write: `2.pow(5)` + | ^^^^^ | = note: `-D clippy::suspicious-xor-used-as-pow` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::suspicious_xor_used_as_pow)]` +help: did you mean to write + | +LL | let _ = 2.pow(5); + | ~~~~~~~~ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:22:13 | LL | let _ = 2i32 ^ 9i32; - | ^^^^^^^^^^^ help: did you mean to write: `2i32.pow(9i32)` + | ^^^^^^^^^^^ + | +help: did you mean to write + | +LL | let _ = 2i32.pow(9i32); + | ~~~~~~~~~~~~~~ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:24:13 | LL | let _ = 2i32 ^ 2i32; - | ^^^^^^^^^^^ help: did you mean to write: `2i32.pow(2i32)` + | ^^^^^^^^^^^ + | +help: did you mean to write + | +LL | let _ = 2i32.pow(2i32); + | ~~~~~~~~~~~~~~ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:26:13 | LL | let _ = 50i32 ^ 3i32; - | ^^^^^^^^^^^^ help: did you mean to write: `50i32.pow(3i32)` + | ^^^^^^^^^^^^ + | +help: did you mean to write + | +LL | let _ = 50i32.pow(3i32); + | ~~~~~~~~~~~~~~~ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:28:13 | LL | let _ = 5i32 ^ 8i32; - | ^^^^^^^^^^^ help: did you mean to write: `5i32.pow(8i32)` + | ^^^^^^^^^^^ + | +help: did you mean to write + | +LL | let _ = 5i32.pow(8i32); + | ~~~~~~~~~~~~~~ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:30:13 | LL | let _ = 2i32 ^ 32i32; - | ^^^^^^^^^^^^ help: did you mean to write: `2i32.pow(32i32)` + | ^^^^^^^^^^^^ + | +help: did you mean to write + | +LL | let _ = 2i32.pow(32i32); + | ~~~~~~~~~~~~~~~ error: `^` is not the exponentiation operator --> tests/ui/suspicious_xor_used_as_pow.rs:13:9 | LL | 1 ^ 2 // should warn even if inside macro - | ^^^^^ help: did you mean to write: `1.pow(2)` + | ^^^^^ ... LL | macro_test_inside!(); | -------------------- in this macro invocation | = note: this error originates in the macro `macro_test_inside` (in Nightly builds, run with -Z macro-backtrace for more info) +help: did you mean to write + | +LL | 1.pow(2) // should warn even if inside macro + | ~~~~~~~~ error: aborting due to 7 previous errors diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index aae4f8ac47f82..f149bfb77740c 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -1,5 +1,5 @@ //@aux-build:proc_macros.rs - +#![feature(try_blocks)] #![deny(clippy::try_err)] #![allow( clippy::unnecessary_wraps, @@ -152,3 +152,11 @@ pub fn try_return(x: bool) -> Result<i32, i32> { } Ok(0) } + +// Test that the lint is suppressed in try block. +pub fn try_block() -> Result<(), i32> { + let _: Result<_, i32> = try { + Err(1)?; + }; + Ok(()) +} diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 927eccf2d54c5..841ec6b5d5c77 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -1,5 +1,5 @@ //@aux-build:proc_macros.rs - +#![feature(try_blocks)] #![deny(clippy::try_err)] #![allow( clippy::unnecessary_wraps, @@ -152,3 +152,11 @@ pub fn try_return(x: bool) -> Result<i32, i32> { } Ok(0) } + +// Test that the lint is suppressed in try block. +pub fn try_block() -> Result<(), i32> { + let _: Result<_, i32> = try { + Err(1)?; + }; + Ok(()) +} diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index 58729a97d57bb..0cc77a8775d57 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -1,7 +1,7 @@ #![warn(clippy::uninit_vec)] -use std::mem::MaybeUninit; use std::cell::UnsafeCell; +use std::mem::MaybeUninit; #[derive(Default)] struct MyVec { diff --git a/tests/ui/unneeded_field_pattern.rs b/tests/ui/unneeded_field_pattern.rs index 0dc21f4ce945d..1d42f81711bd6 100644 --- a/tests/ui/unneeded_field_pattern.rs +++ b/tests/ui/unneeded_field_pattern.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::unneeded_field_pattern)] -#![allow(dead_code, unused)] +#![allow(dead_code, unused, clippy::single_match)] #[macro_use] extern crate proc_macros; diff --git a/tests/ui/unused_result_ok.fixed b/tests/ui/unused_result_ok.fixed new file mode 100644 index 0000000000000..e78fde5c9e3cc --- /dev/null +++ b/tests/ui/unused_result_ok.fixed @@ -0,0 +1,40 @@ +//@aux-build:proc_macros.rs +#![warn(clippy::unused_result_ok)] +#![allow(dead_code)] + +#[macro_use] +extern crate proc_macros; + +fn bad_style(x: &str) { + let _ = x.parse::<u32>(); +} + +fn good_style(x: &str) -> Option<u32> { + x.parse::<u32>().ok() +} + +#[rustfmt::skip] +fn strange_parse(x: &str) { + let _ = x . parse::<i32>(); +} + +macro_rules! v { + () => { + Ok::<(), ()>(()) + }; +} + +macro_rules! w { + () => { + let _ = Ok::<(), ()>(()); + }; +} + +fn main() { + let _ = v!(); + w!(); + + external! { + Ok::<(),()>(()).ok(); + }; +} diff --git a/tests/ui/unused_result_ok.rs b/tests/ui/unused_result_ok.rs new file mode 100644 index 0000000000000..117d64c4cec60 --- /dev/null +++ b/tests/ui/unused_result_ok.rs @@ -0,0 +1,40 @@ +//@aux-build:proc_macros.rs +#![warn(clippy::unused_result_ok)] +#![allow(dead_code)] + +#[macro_use] +extern crate proc_macros; + +fn bad_style(x: &str) { + x.parse::<u32>().ok(); +} + +fn good_style(x: &str) -> Option<u32> { + x.parse::<u32>().ok() +} + +#[rustfmt::skip] +fn strange_parse(x: &str) { + x . parse::<i32>() . ok (); +} + +macro_rules! v { + () => { + Ok::<(), ()>(()) + }; +} + +macro_rules! w { + () => { + Ok::<(), ()>(()).ok(); + }; +} + +fn main() { + v!().ok(); + w!(); + + external! { + Ok::<(),()>(()).ok(); + }; +} diff --git a/tests/ui/unused_result_ok.stderr b/tests/ui/unused_result_ok.stderr new file mode 100644 index 0000000000000..241e0c71261e3 --- /dev/null +++ b/tests/ui/unused_result_ok.stderr @@ -0,0 +1,52 @@ +error: ignoring a result with `.ok()` is misleading + --> tests/ui/unused_result_ok.rs:9:5 + | +LL | x.parse::<u32>().ok(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unused-result-ok` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unused_result_ok)]` +help: consider using `let _ =` and removing the call to `.ok()` instead + | +LL | let _ = x.parse::<u32>(); + | ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: ignoring a result with `.ok()` is misleading + --> tests/ui/unused_result_ok.rs:18:5 + | +LL | x . parse::<i32>() . ok (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `let _ =` and removing the call to `.ok()` instead + | +LL | let _ = x . parse::<i32>(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: ignoring a result with `.ok()` is misleading + --> tests/ui/unused_result_ok.rs:34:5 + | +LL | v!().ok(); + | ^^^^^^^^^ + | +help: consider using `let _ =` and removing the call to `.ok()` instead + | +LL | let _ = v!(); + | ~~~~~~~~~~~~ + +error: ignoring a result with `.ok()` is misleading + --> tests/ui/unused_result_ok.rs:29:9 + | +LL | Ok::<(), ()>(()).ok(); + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | w!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `w` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider using `let _ =` and removing the call to `.ok()` instead + | +LL | let _ = Ok::<(), ()>(()); + | ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/unwrap_expect_used.rs b/tests/ui/unwrap_expect_used.rs index 96368a0705361..a56bd0a8d0790 100644 --- a/tests/ui/unwrap_expect_used.rs +++ b/tests/ui/unwrap_expect_used.rs @@ -31,7 +31,7 @@ fn main() { // Don't trigger on unwrap_err on an option Some(3).unwrap_err(); - Some(3).expect_err("Hellow none!"); + Some(3).expect_err("Hello none!"); // Issue #11245: The `Err` variant can never be constructed so do not lint this. let x: Result<(), !> = Ok(()); diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index 59b5c858d0460..b8087c6e000f5 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -456,6 +456,15 @@ fn fn_once_closure() { }); } +fn issue13123() { + let mut it = 0..20; + 'label: for n in it { + if n % 25 == 0 { + break 'label; + } + } +} + fn main() { let mut it = 0..20; for _ in it { diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 559513d56946d..8e02f59b51265 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -456,6 +456,15 @@ fn fn_once_closure() { }); } +fn issue13123() { + let mut it = 0..20; + 'label: while let Some(n) = it.next() { + if n % 25 == 0 { + break 'label; + } + } +} + fn main() { let mut it = 0..20; while let Some(..) = it.next() { diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 8ff1f23644b16..d96b26acf345d 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -160,8 +160,14 @@ LL | while let Some(x) = it.next() { error: this loop could be written as a `for` loop --> tests/ui/while_let_on_iterator.rs:461:5 | +LL | 'label: while let Some(n) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'label: for n in it` + +error: this loop could be written as a `for` loop + --> tests/ui/while_let_on_iterator.rs:470:5 + | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` -error: aborting due to 27 previous errors +error: aborting due to 28 previous errors diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index eba5405e67ed2..6832833393766 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::single_match_else)] diff --git a/tests/workspace_test/path_dep/Cargo.toml b/tests/workspace_test/path_dep/Cargo.toml index 85a91cd2decd5..98b4fb7aa5042 100644 --- a/tests/workspace_test/path_dep/Cargo.toml +++ b/tests/workspace_test/path_dep/Cargo.toml @@ -1,3 +1,6 @@ [package] name = "path_dep" version = "0.1.0" + +[features] +primary_package_test = [] diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 0c0f28e4fbd3d..300c9de178f2b 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -541,7 +541,7 @@ <h1>Clippy Lints</h1> <div class="col-12 col-md-5 search-control"> <div class="input-group"> <label class="input-group-addon" id="filter-label" for="search-input">Filter:</label> - <input type="text" class="form-control filter-input" placeholder="Keywords or search string" id="search-input" + <input type="text" class="form-control filter-input" placeholder="Keywords or search string (`S` or `/` to focus)" id="search-input" ng-model="search" ng-blur="updatePath()" ng-keyup="$event.keyCode == 13 && updatePath()" ng-model-options="{debounce: 50}" /> <span class="input-group-btn"> @@ -605,7 +605,7 @@ <h2 class="panel-title"> <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a> </div> <!-- Jump to source --> - <div class="lint-additional-info-item"> + <div class="lint-additional-info-item" ng-if="lint.id_span"> <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/clippy_lints/{{lint.id_span.path}}#L{{lint.id_span.line}}">View Source</a> </div> </div> diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index 661f80a6d3468..ed1e090e1b540 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -1,5 +1,5 @@ (function () { - var md = window.markdownit({ + const md = window.markdownit({ html: true, linkify: true, typographer: true, @@ -17,7 +17,7 @@ }); function scrollToLint(lintId) { - var target = document.getElementById(lintId); + const target = document.getElementById(lintId); if (!target) { return; } @@ -25,21 +25,17 @@ } function scrollToLintByURL($scope, $location) { - var removeListener = $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) { + const removeListener = $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) { scrollToLint($location.path().substring(1)); removeListener(); }); } function selectGroup($scope, selectedGroup) { - var groups = $scope.groups; - for (var group in groups) { + const groups = $scope.groups; + for (const group in groups) { if (groups.hasOwnProperty(group)) { - if (group === selectedGroup) { - groups[group] = true; - } else { - groups[group] = false; - } + groups[group] = group === selectedGroup; } } } @@ -108,7 +104,7 @@ }) .controller("lintList", function ($scope, $http, $location, $timeout) { // Level filter - var LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true}; + const LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true}; $scope.levels = { ...LEVEL_FILTERS_DEFAULT }; $scope.byLevels = function (lint) { return $scope.levels[lint.level]; @@ -367,7 +363,7 @@ } $scope.clearVersionFilters = function () { - for (let filter in $scope.versionFilters) { + for (const filter in $scope.versionFilters) { $scope.versionFilters[filter] = { enabled: false, minorVersion: null }; } } @@ -378,7 +374,7 @@ $scope.updateVersionFilters = function() { for (const filter in $scope.versionFilters) { - let minorVersion = $scope.versionFilters[filter].minorVersion; + const minorVersion = $scope.versionFilters[filter].minorVersion; // 1.29.0 and greater if (minorVersion && minorVersion > 28) { @@ -391,14 +387,14 @@ } $scope.byVersion = function(lint) { - let filters = $scope.versionFilters; + const filters = $scope.versionFilters; for (const filter in filters) { if (filters[filter].enabled) { - let minorVersion = filters[filter].minorVersion; + const minorVersion = filters[filter].minorVersion; // Strip the "pre " prefix for pre 1.29.0 lints - let lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; - let lintMinorVersion = lintVersion.substring(2, 4); + const lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; + const lintMinorVersion = lintVersion.substring(2, 4); switch (filter) { // "=" gets the highest priority, since all filters are inclusive @@ -441,8 +437,8 @@ // Search the description // The use of `for`-loops instead of `foreach` enables us to return early - let terms = searchStr.split(" "); - let docsLowerCase = lint.docs.toLowerCase(); + const terms = searchStr.split(" "); + const docsLowerCase = lint.docs.toLowerCase(); for (index = 0; index < terms.length; index++) { // This is more likely and will therefore be checked first if (docsLowerCase.indexOf(terms[index]) !== -1) { @@ -479,7 +475,7 @@ const clipboard = document.getElementById("clipboard-" + lint.id); if (clipboard) { let resetClipboardTimeout = null; - let resetClipboardIcon = clipboard.innerHTML; + const resetClipboardIcon = clipboard.innerHTML; function resetClipboard() { resetClipboardTimeout = null; @@ -511,7 +507,7 @@ $scope.data = data; $scope.loading = false; - var selectedGroup = getQueryVariable("sel"); + const selectedGroup = getQueryVariable("sel"); if (selectedGroup) { selectGroup($scope, selectedGroup.toLowerCase()); } @@ -519,7 +515,7 @@ scrollToLintByURL($scope, $location); setTimeout(function () { - var el = document.getElementById('filter-input'); + const el = document.getElementById('filter-input'); if (el) { el.focus() } }, 0); }) @@ -531,10 +527,10 @@ })(); function getQueryVariable(variable) { - var query = window.location.search.substring(1); - var vars = query.split('&'); - for (var i = 0; i < vars.length; i++) { - var pair = vars[i].split('='); + const query = window.location.search.substring(1); + const vars = query.split('&'); + for (const entry of vars) { + const pair = entry.split('='); if (decodeURIComponent(pair[0]) == variable) { return decodeURIComponent(pair[1]); } @@ -579,6 +575,32 @@ function setTheme(theme, store) { } } +function handleShortcut(ev) { + if (ev.ctrlKey || ev.altKey || ev.metaKey) { + return; + } + + if (document.activeElement.tagName === "INPUT") { + if (ev.key === "Escape") { + document.activeElement.blur(); + } + } else { + switch (ev.key) { + case "s": + case "S": + case "/": + ev.preventDefault(); // To prevent the key to be put into the input. + document.getElementById("search-input").focus(); + break; + default: + break; + } + } +} + +document.addEventListener("keypress", handleShortcut); +document.addEventListener("keydown", handleShortcut); + // loading the theme after the initial load const prefersDark = window.matchMedia("(prefers-color-scheme: dark)"); const theme = localStorage.getItem('clippy-lint-list-theme'); From 9948b423a0e602e514e63ead9dfebdfa9acb0f03 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk <alexsemenyuk88@gmail.com> Date: Fri, 9 Aug 2024 01:35:28 +0500 Subject: [PATCH 15/54] Fix confusing message in double_must_use lint --- clippy_lints/src/functions/must_use.rs | 4 ++-- tests/ui/double_must_use.rs | 8 ++++---- tests/ui/double_must_use.stderr | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index b179d7b52492c..97be38c4a7dd6 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -129,9 +129,9 @@ fn check_needless_must_use( cx, DOUBLE_MUST_USE, fn_header_span, - "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`", + "this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`", None, - "either add some descriptive text or remove the attribute", + "either add some descriptive message or remove the attribute", ); } } diff --git a/tests/ui/double_must_use.rs b/tests/ui/double_must_use.rs index 615de3e24743c..4460aeb075bf6 100644 --- a/tests/ui/double_must_use.rs +++ b/tests/ui/double_must_use.rs @@ -3,19 +3,19 @@ #[must_use] pub fn must_use_result() -> Result<(), ()> { - //~^ ERROR: this function has an empty `#[must_use]` attribute, but returns a type already + //~^ ERROR: this function has a `#[must_use]` attribute with no message, but returns a type already unimplemented!(); } #[must_use] pub fn must_use_tuple() -> (Result<(), ()>, u8) { - //~^ ERROR: this function has an empty `#[must_use]` attribute, but returns a type already + //~^ ERROR: this function has a `#[must_use]` attribute with no message, but returns a type already unimplemented!(); } #[must_use] pub fn must_use_array() -> [Result<(), ()>; 1] { - //~^ ERROR: this function has an empty `#[must_use]` attribute, but returns a type already + //~^ ERROR: this function has a `#[must_use]` attribute with no message, but returns a type already unimplemented!(); } @@ -32,7 +32,7 @@ async fn async_must_use() -> usize { #[must_use] async fn async_must_use_result() -> Result<(), ()> { - //~^ ERROR: this function has an empty `#[must_use]` attribute, but returns a type already + //~^ ERROR: this function has a `#[must_use]` attribute with no message, but returns a type already Ok(()) } diff --git a/tests/ui/double_must_use.stderr b/tests/ui/double_must_use.stderr index 0f2154ecbcfab..b26d1e48a8b86 100644 --- a/tests/ui/double_must_use.stderr +++ b/tests/ui/double_must_use.stderr @@ -1,36 +1,36 @@ -error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` +error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]` --> tests/ui/double_must_use.rs:5:1 | LL | pub fn must_use_result() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: either add some descriptive text or remove the attribute + = help: either add some descriptive message or remove the attribute = note: `-D clippy::double-must-use` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::double_must_use)]` -error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` +error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]` --> tests/ui/double_must_use.rs:11:1 | LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: either add some descriptive text or remove the attribute + = help: either add some descriptive message or remove the attribute -error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` +error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]` --> tests/ui/double_must_use.rs:17:1 | LL | pub fn must_use_array() -> [Result<(), ()>; 1] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: either add some descriptive text or remove the attribute + = help: either add some descriptive message or remove the attribute -error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` +error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]` --> tests/ui/double_must_use.rs:34:1 | LL | async fn async_must_use_result() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: either add some descriptive text or remove the attribute + = help: either add some descriptive message or remove the attribute error: aborting due to 4 previous errors From e785219238e73f353f392afe30073180c5b2c5d6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote <n.nethercote@gmail.com> Date: Thu, 8 Aug 2024 17:18:20 +1000 Subject: [PATCH 16/54] Shrink `TyKind::FnPtr`. By splitting the `FnSig` within `TyKind::FnPtr` into `FnSigTys` and `FnHeader`, which can be packed more efficiently. This reduces the size of the hot `TyKind` type from 32 bytes to 24 bytes on 64-bit platforms. This reduces peak memory usage by a few percent on some benchmarks. It also reduces cache misses and page faults similarly, though this doesn't translate to clear cycles or wall-time improvements on CI. --- clippy_lints/src/casts/fn_to_numeric_cast.rs | 2 +- clippy_lints/src/casts/fn_to_numeric_cast_any.rs | 2 +- clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 2 +- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/eta_reduction.rs | 2 +- clippy_lints/src/methods/map_flatten.rs | 2 +- clippy_lints/src/mixed_read_write_in_expression.rs | 2 +- clippy_lints/src/multiple_unsafe_ops_per_block.rs | 2 +- clippy_lints/src/mut_reference.rs | 2 +- clippy_utils/src/ty.rs | 4 ++-- clippy_utils/src/visitors.rs | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/casts/fn_to_numeric_cast.rs b/clippy_lints/src/casts/fn_to_numeric_cast.rs index f263bec1576d0..dbe03e4ae8095 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -15,7 +15,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, } match cast_from.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { + ty::FnDef(..) | ty::FnPtr(..) => { let mut applicability = Applicability::MaybeIncorrect; let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs index 75de53f73ee7a..5dc6df1e907d0 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, _ => { /* continue to checks */ }, } - if let ty::FnDef(..) | ty::FnPtr(_) = cast_from.kind() { + if let ty::FnDef(..) | ty::FnPtr(..) = cast_from.kind() { let mut applicability = Applicability::MaybeIncorrect; let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs index 0e11bcfb8ecdb..dfbae1618ac62 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, _ => return, } match cast_from.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { + ty::FnDef(..) | ty::FnPtr(..) => { let mut applicability = Applicability::MaybeIncorrect; let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index a74b3a8c8362c..05c3cd3c81407 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -236,7 +236,7 @@ fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<PolyFnSig<' // We can't use `Ty::fn_sig` because it automatically performs args, this may result in FNs. match node_ty.kind() { ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id).instantiate_identity()), - ty::FnPtr(fn_sig) => Some(*fn_sig), + ty::FnPtr(sig_tys, hdr) => Some(sig_tys.with(*hdr)), _ => None, } } diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index d0cb24884686a..0e55d3db469ae 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -872,7 +872,7 @@ impl TyCoercionStability { | ty::Pat(..) | ty::Float(_) | ty::RawPtr(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Str | ty::Slice(..) | ty::Adt(..) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 5a7226d590c4d..a7e831fdc42a8 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -158,7 +158,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc cx.tcx.fn_sig(def).skip_binder().skip_binder() }, - ty::FnPtr(sig) => sig.skip_binder(), + ty::FnPtr(sig_tys, hdr) => sig_tys.with(*hdr).skip_binder(), ty::Closure(_, subs) => cx .tcx .signature_unclosure(subs.as_closure().sig(), Safety::Safe) diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index def8be2ef73dc..22a03825194e0 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -58,7 +58,7 @@ fn try_get_caller_ty_name_and_method_name( fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool { let map_closure_ty = cx.typeck_results().expr_ty(map_arg); match map_closure_ty.kind() { - ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { + ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(..) => { let map_closure_sig = match map_closure_ty.kind() { ty::Closure(_, args) => args.as_closure().sig(), _ => map_closure_ty.fn_sig(cx.tcx), diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 6964d8c8dbb33..0b3769ecb7cc4 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -166,7 +166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { ExprKind::Call(func, _) => { let typ = self.cx.typeck_results().expr_ty(func); match typ.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { + ty::FnDef(..) | ty::FnPtr(..) => { let sig = typ.fn_sig(self.cx.tcx); if self.cx.tcx.instantiate_bound_regions_with_erased(sig).output().kind() == &ty::Never { self.report_diverging_sub_expr(e); diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index da74a7c7145a6..0bde0da3cd814 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -130,7 +130,7 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::Call(path_expr, _) => { let sig = match *cx.typeck_results().expr_ty(path_expr).kind() { ty::FnDef(id, _) => cx.tcx.fn_sig(id).skip_binder(), - ty::FnPtr(sig) => sig, + ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr), _ => return Continue(Descend::Yes), }; if sig.safety() == Safety::Unsafe { diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 0a3b769c3e604..3c0f06f66d105 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -79,7 +79,7 @@ fn check_arguments<'tcx>( fn_kind: &str, ) { match type_definition.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { + ty::FnDef(..) | ty::FnPtr(..) => { let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); for (argument, parameter) in iter::zip(arguments, parameters) { match parameter.kind() { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index bd48990aea95f..2f6faba073e95 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -541,7 +541,7 @@ pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { /// Returns `true` if the given type is an `unsafe` function. pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind() { - ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).safety() == Safety::Unsafe, + ty::FnDef(..) | ty::FnPtr(..) => ty.fn_sig(cx.tcx).safety() == Safety::Unsafe, _ => false, } } @@ -721,7 +721,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t cx.tcx.item_super_predicates(def_id).iter_instantiated(cx.tcx, args), cx.tcx.opt_parent(def_id), ), - ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), + ty::FnPtr(sig_tys, hdr) => Some(ExprFnSig::Sig(sig_tys.with(hdr), None)), ty::Dynamic(bounds, _, _) => { let lang_items = cx.tcx.lang_items(); match bounds.principal() { diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 2a5d3536ff6b7..e5b6d3965e930 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -441,7 +441,7 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe => { self.is_unsafe = true; }, - ty::FnPtr(sig) if sig.safety() == Safety::Unsafe => self.is_unsafe = true, + ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => self.is_unsafe = true, _ => walk_expr(self, e), }, ExprKind::Path(ref p) From d85cf0bacd8ea565557317940d878a535189a6d9 Mon Sep 17 00:00:00 2001 From: rzvxa <rzvxa@protonmail.com> Date: Fri, 9 Aug 2024 12:18:56 +0330 Subject: [PATCH 17/54] use `fulfill_or_allowed` over `is_lint_allowed`. --- .../src/inconsistent_struct_constructor.rs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index da289225509bf..d386bfca6baa1 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_lint_allowed; +use clippy_utils::fulfill_or_allowed; use clippy_utils::source::snippet; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -74,7 +74,6 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { && adt_def.is_struct() && let Some(local_def_id) = adt_def.did().as_local() && let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) - && !is_lint_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, ty_hir_id) && let Some(variant) = adt_def.variants().iter().next() { let mut def_order_map = FxHashMap::default(); @@ -107,15 +106,17 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { snippet(cx, qpath.span(), ".."), ); - span_lint_and_sugg( - cx, - INCONSISTENT_STRUCT_CONSTRUCTOR, - expr.span, - "struct constructor field order is inconsistent with struct definition field order", - "try", - sugg, - Applicability::MachineApplicable, - ); + if !fulfill_or_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, Some(ty_hir_id)) { + span_lint_and_sugg( + cx, + INCONSISTENT_STRUCT_CONSTRUCTOR, + expr.span, + "struct constructor field order is inconsistent with struct definition field order", + "try", + sugg, + Applicability::MachineApplicable, + ); + } } } } From b32a0176faa3ff2aba07f38b7d7cf72304d6d4e0 Mon Sep 17 00:00:00 2001 From: Alex Macleod <alex@macleod.io> Date: Fri, 9 Aug 2024 13:00:24 +0000 Subject: [PATCH 18/54] Replace `rustc_semver` with `rustc_session::RustcVersion` --- clippy_config/Cargo.toml | 1 - clippy_config/src/lib.rs | 1 + clippy_config/src/msrvs.rs | 16 ++++++++-------- clippy_lints/Cargo.toml | 1 - clippy_lints/src/approx_const.rs | 3 +-- clippy_lints/src/incompatible_msrv.rs | 15 +++++++-------- clippy_lints/src/manual_retain.rs | 3 +-- clippy_lints/src/std_instead_of_core.rs | 5 +---- .../internal_lints/lint_without_lint_pass.rs | 10 +++++++--- clippy_utils/Cargo.toml | 1 - clippy_utils/src/qualify_min_const_fn.rs | 7 +------ tests/ui/min_rust_version_invalid_attr.rs | 2 +- tests/ui/min_rust_version_invalid_attr.stderr | 4 ++-- 13 files changed, 30 insertions(+), 39 deletions(-) diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index d5b28e2532371..5c4e0761dbca7 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" [dependencies] itertools = "0.12" -rustc-semver = "1.1" serde = { version = "1.0", features = ["derive"] } toml = "0.7.3" diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index d2246f12029b3..3afe599c7b6e4 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -14,6 +14,7 @@ )] extern crate rustc_ast; +extern crate rustc_attr; extern crate rustc_data_structures; #[allow(unused_extern_crates)] extern crate rustc_driver; diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index fc56ac5179686..5b9707efd2634 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -1,6 +1,6 @@ use rustc_ast::Attribute; -use rustc_semver::RustcVersion; -use rustc_session::Session; +use rustc_attr::parse_version; +use rustc_session::{RustcVersion, Session}; use rustc_span::{sym, Symbol}; use serde::Deserialize; use std::fmt; @@ -10,7 +10,7 @@ macro_rules! msrv_aliases { $($name:ident),* $(,)? })*) => { $($( - pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch); + pub const $name: RustcVersion = RustcVersion { major: $major, minor :$minor, patch: $patch }; )*)* }; } @@ -81,9 +81,9 @@ impl<'de> Deserialize<'de> for Msrv { D: serde::Deserializer<'de>, { let v = String::deserialize(deserializer)?; - RustcVersion::parse(&v) + parse_version(Symbol::intern(&v)) .map(|v| Msrv { stack: vec![v] }) - .map_err(|_| serde::de::Error::custom("not a valid Rust version")) + .ok_or_else(|| serde::de::Error::custom("not a valid Rust version")) } } @@ -95,7 +95,7 @@ impl Msrv { pub fn read_cargo(&mut self, sess: &Session) { let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") .ok() - .and_then(|v| RustcVersion::parse(&v).ok()); + .and_then(|v| parse_version(Symbol::intern(&v))); match (self.current(), cargo_msrv) { (None, Some(cargo_msrv)) => self.stack = vec![cargo_msrv], @@ -115,7 +115,7 @@ impl Msrv { } pub fn meets(&self, required: RustcVersion) -> bool { - self.current().map_or(true, |version| version.meets(required)) + self.current().map_or(true, |msrv| msrv >= required) } fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> { @@ -131,7 +131,7 @@ impl Msrv { } if let Some(msrv) = msrv_attr.value_str() { - if let Ok(version) = RustcVersion::parse(msrv.as_str()) { + if let Some(version) = parse_version(msrv) { return Some(version); } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 99ed93468a01a..fbd4566da58c7 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -25,7 +25,6 @@ regex = { version = "1.5", optional = true } unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } semver = "1.0" -rustc-semver = "1.1" url = "2.2" [dev-dependencies] diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 3b4cc1134802d..56f5e903dc3e2 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -4,8 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; -use rustc_session::impl_lint_pass; +use rustc_session::{impl_lint_pass, RustcVersion}; use rustc_span::symbol; use std::f64::consts as f64; diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 0ef5b803a89d6..2ad045e12686c 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -7,8 +7,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; -use rustc_semver::RustcVersion; -use rustc_session::impl_lint_pass; +use rustc_session::{impl_lint_pass, RustcVersion}; use rustc_span::def_id::DefId; use rustc_span::{ExpnKind, Span}; @@ -65,18 +64,18 @@ impl IncompatibleMsrv { StabilityLevel::Stable { since: StableSince::Version(version), .. - } => Some(RustcVersion::new( - version.major.into(), - version.minor.into(), - version.patch.into(), - )), + } => Some(version), _ => None, }) { version } else if let Some(parent_def_id) = tcx.opt_parent(def_id) { self.get_def_id_version(tcx, parent_def_id) } else { - RustcVersion::new(1, 0, 0) + RustcVersion { + major: 1, + minor: 0, + patch: 0, + } }; self.is_above_msrv.insert(def_id, version); version diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 09f6362b4ddc8..d549470ed47e1 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -9,8 +9,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::ExprKind::Assign; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; -use rustc_session::impl_lint_pass; +use rustc_session::{impl_lint_pass, RustcVersion}; use rustc_span::symbol::sym; use rustc_span::Span; diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 974e21df817a8..44283a49e84b9 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -9,7 +9,6 @@ use rustc_hir::def_id::DefId; use rustc_hir::{HirId, Path, PathSegment}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; use rustc_span::{sym, Span}; @@ -185,9 +184,7 @@ fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: &Msrv) -> bool { } = stability.level { let stable = match since { - StableSince::Version(v) => { - msrv.meets(RustcVersion::new(v.major.into(), v.minor.into(), v.patch.into())) - }, + StableSince::Version(v) => msrv.meets(v), StableSince::Current => msrv.current().is_none(), StableSince::Err => 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 index df342e48d637f..20526113d69e3 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -9,7 +9,6 @@ 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::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; @@ -92,7 +91,12 @@ pub struct LintWithoutLintPass { registered_lints: FxHashSet<Symbol>, } -impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]); +impl_lint_pass!(LintWithoutLintPass => [ + DEFAULT_LINT, + LINT_WITHOUT_LINT_PASS, + INVALID_CLIPPY_VERSION_ATTRIBUTE, + MISSING_CLIPPY_VERSION_ATTRIBUTE, +]); impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -220,7 +224,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<' return; } - if RustcVersion::parse(value.as_str()).is_err() { + if rustc_attr::parse_version(value).is_none() { span_lint_and_help( cx, INVALID_CLIPPY_VERSION_ATTRIBUTE, diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 9fefd94d339be..629ce9d04d432 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -8,7 +8,6 @@ publish = false clippy_config = { path = "../clippy_config" } arrayvec = { version = "0.7", default-features = false } itertools = "0.12" -rustc-semver = "1.1" # FIXME(f16_f128): remove when no longer needed for parsing rustc_apfloat = "0.2.0" diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index f206b2ceebcb1..92b74355607a7 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -18,7 +18,6 @@ use rustc_middle::mir::{ use rustc_middle::traits::{BuiltinImplSource, ImplSource, ObligationCause}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, GenericArgKind, TraitRef, Ty, TyCtxt}; -use rustc_semver::RustcVersion; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::traits::{ObligationCtxt, SelectionContext}; @@ -391,11 +390,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool { StableSince::Err => return false, }; - msrv.meets(RustcVersion::new( - u32::from(const_stab_rust_version.major), - u32::from(const_stab_rust_version.minor), - u32::from(const_stab_rust_version.patch), - )) + msrv.meets(const_stab_rust_version) } else { // Unstable const fn with the feature enabled. msrv.current().is_none() diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs index 2dccadd9fceec..c8409d78ed77c 100644 --- a/tests/ui/min_rust_version_invalid_attr.rs +++ b/tests/ui/min_rust_version_invalid_attr.rs @@ -17,7 +17,7 @@ mod multiple { //~^ ERROR: `clippy::msrv` is defined multiple times mod foo { - #![clippy::msrv = "1"] + #![clippy::msrv = "1.0"] #![clippy::msrv = "1.0.0"] //~^ ERROR: `clippy::msrv` is defined multiple times } diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr index b4cb1b5713f90..dbc276ed89df3 100644 --- a/tests/ui/min_rust_version_invalid_attr.stderr +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -31,8 +31,8 @@ LL | #![clippy::msrv = "1.0.0"] note: first definition found here --> tests/ui/min_rust_version_invalid_attr.rs:20:9 | -LL | #![clippy::msrv = "1"] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #![clippy::msrv = "1.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors From 9afab36ca9aac3ac8a685ead1982b8ad6e782f70 Mon Sep 17 00:00:00 2001 From: Jason Newcomb <jsnewcomb@pm.me> Date: Fri, 9 Aug 2024 09:01:41 -0400 Subject: [PATCH 19/54] Rename `get_source_text` to `get_source_range`. Add new `get_source_text` which returns a displayable string-like type. --- clippy_lints/src/matches/single_match.rs | 2 +- clippy_utils/src/consts.rs | 2 +- clippy_utils/src/hir_utils.rs | 4 +- clippy_utils/src/source.rs | 71 ++++++++++++++++-------- 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 24dea03601c5f..b6930f7b9d10f 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -22,7 +22,7 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty /// match arms. fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { - if let Some(ff) = span.get_source_text(cx) + if let Some(ff) = span.get_source_range(cx) && let Some(text) = ff.as_str() { text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*") diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index e907e4058e5a2..760d5bc95f7fb 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -687,7 +687,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) && let expr_lo = expr_span.lo() && expr_lo >= span.lo - && let Some(src) = (span.lo..expr_lo).get_source_text(&self.tcx) + && let Some(src) = (span.lo..expr_lo).get_source_range(&self.tcx) && let Some(src) = src.as_str() { use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace}; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index f325e4eaf1544..b0a668329e920 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1191,9 +1191,9 @@ fn eq_span_tokens( pred: impl Fn(TokenKind) -> bool, ) -> bool { fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool { - if let Some(lsrc) = left.get_source_text(cx) + if let Some(lsrc) = left.get_source_range(cx) && let Some(lsrc) = lsrc.as_str() - && let Some(rsrc) = right.get_source_text(cx) + && let Some(rsrc) = right.get_source_range(cx) && let Some(rsrc) = rsrc.as_str() { let pred = |t: &(_, _)| pred(t.0); diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 96dd3c55d37ae..1be1a9b28b9ea 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -16,7 +16,7 @@ use rustc_span::{ }; use std::borrow::Cow; use std::fmt; -use std::ops::Range; +use std::ops::{Deref, Range}; pub trait HasSession { fn sess(&self) -> &Session; @@ -94,10 +94,16 @@ impl IntoSpan for Range<BytePos> { } pub trait SpanRangeExt: SpanRange { + /// Attempts to get a handle to the source text. Returns `None` if either the span is malformed, + /// or the source text is not accessible. + fn get_source_text(self, cx: &impl HasSession) -> Option<SourceText> { + get_source_range(cx.sess().source_map(), self.into_range()).and_then(SourceText::new) + } + /// Gets the source file, and range in the file, of the given span. Returns `None` if the span /// extends through multiple files, or is malformed. - fn get_source_text(self, cx: &impl HasSession) -> Option<SourceFileRange> { - get_source_text(cx.sess().source_map(), self.into_range()) + fn get_source_range(self, cx: &impl HasSession) -> Option<SourceFileRange> { + get_source_range(cx.sess().source_map(), self.into_range()) } /// Calls the given function with the source text referenced and returns the value. Returns @@ -144,21 +150,49 @@ pub trait SpanRangeExt: SpanRange { fn trim_start(self, cx: &impl HasSession) -> Range<BytePos> { trim_start(cx.sess().source_map(), self.into_range()) } +} +impl<T: SpanRange> SpanRangeExt for T {} - /// Writes the referenced source text to the given writer. Will return `Err` if the source text - /// could not be retrieved. - fn write_source_text_to(self, cx: &impl HasSession, dst: &mut impl fmt::Write) -> fmt::Result { - write_source_text_to(cx.sess().source_map(), self.into_range(), dst) +/// Handle to a range of text in a source file. +pub struct SourceText(SourceFileRange); +impl SourceText { + /// Takes ownership of the source file handle if the source text is accessible. + pub fn new(text: SourceFileRange) -> Option<Self> { + if text.as_str().is_some() { + Some(Self(text)) + } else { + None + } + } + + /// Gets the source text. + pub fn as_str(&self) -> &str { + self.0.as_str().unwrap() } - /// Extracts the referenced source text as an owned string. - fn source_text_to_string(self, cx: &impl HasSession) -> Option<String> { - self.with_source_text(cx, ToOwned::to_owned) + /// Converts this into an owned string. + pub fn to_owned(&self) -> String { + self.as_str().to_owned() + } +} +impl Deref for SourceText { + type Target = str; + fn deref(&self) -> &Self::Target { + self.as_str() + } +} +impl AsRef<str> for SourceText { + fn as_ref(&self) -> &str { + self.as_str() + } +} +impl fmt::Display for SourceText { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_str().fmt(f) } } -impl<T: SpanRange> SpanRangeExt for T {} -fn get_source_text(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> { +fn get_source_range(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> { let start = sm.lookup_byte_offset(sp.start); let end = sm.lookup_byte_offset(sp.end); if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos { @@ -169,7 +203,7 @@ fn get_source_text(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange } fn with_source_text<T>(sm: &SourceMap, sp: Range<BytePos>, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> { - if let Some(src) = get_source_text(sm, sp) + if let Some(src) = get_source_range(sm, sp) && let Some(src) = src.as_str() { Some(f(src)) @@ -183,7 +217,7 @@ fn with_source_text_and_range<T>( sp: Range<BytePos>, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T, ) -> Option<T> { - if let Some(src) = get_source_text(sm, sp) + if let Some(src) = get_source_range(sm, sp) && let Some(text) = &src.sf.src { Some(f(text, src.range)) @@ -198,7 +232,7 @@ fn map_range( sp: Range<BytePos>, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>, ) -> Option<Range<BytePos>> { - if let Some(src) = get_source_text(sm, sp.clone()) + if let Some(src) = get_source_range(sm, sp.clone()) && let Some(text) = &src.sf.src && let Some(range) = f(text, src.range.clone()) { @@ -232,13 +266,6 @@ fn trim_start(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> { .unwrap_or(sp) } -fn write_source_text_to(sm: &SourceMap, sp: Range<BytePos>, dst: &mut impl fmt::Write) -> fmt::Result { - match with_source_text(sm, sp, |src| dst.write_str(src)) { - Some(x) => x, - None => Err(fmt::Error), - } -} - pub struct SourceFileRange { pub sf: Lrc<SourceFile>, pub range: Range<usize>, From dda9eb9fa8c87dc3d807189491a7730810d005a4 Mon Sep 17 00:00:00 2001 From: Ben Kimock <kimockb@gmail.com> Date: Thu, 11 Jul 2024 19:41:48 -0400 Subject: [PATCH 20/54] Paper over the clippy ICE --- tests/ui/borrow_interior_mutable_const/others.rs | 3 +-- tests/ui/borrow_interior_mutable_const/others.stderr | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs index 0ea93dd84625c..452d1b198133e 100644 --- a/tests/ui/borrow_interior_mutable_const/others.rs +++ b/tests/ui/borrow_interior_mutable_const/others.rs @@ -10,7 +10,7 @@ use std::sync::Once; const ATOMIC: AtomicUsize = AtomicUsize::new(5); const CELL: Cell<usize> = Cell::new(6); -const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7); +const ATOMIC_TUPLE: ([AtomicUsize; 1], Option<Box<AtomicUsize>>, u8) = ([ATOMIC], None, 7); const INTEGER: u8 = 8; const STRING: String = String::new(); const STR: &str = "012345"; @@ -74,7 +74,6 @@ fn main() { let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR: interior mutability let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR: interior mutability let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR: interior mutability - let _ = &*ATOMIC_TUPLE.1; let _ = &ATOMIC_TUPLE.2; let _ = (&&&&ATOMIC_TUPLE).0; let _ = (&&&&ATOMIC_TUPLE).2; diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr index 33c774667f942..9a9028c864986 100644 --- a/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/tests/ui/borrow_interior_mutable_const/others.stderr @@ -92,7 +92,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:82:13 + --> tests/ui/borrow_interior_mutable_const/others.rs:81:13 | LL | let _ = ATOMIC_TUPLE.0[0]; | ^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:87:5 + --> tests/ui/borrow_interior_mutable_const/others.rs:86:5 | LL | CELL.set(2); | ^^^^ @@ -108,7 +108,7 @@ LL | CELL.set(2); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:88:16 + --> tests/ui/borrow_interior_mutable_const/others.rs:87:16 | LL | assert_eq!(CELL.get(), 6); | ^^^^ From f9509d3574a998bbfb664c5768d183f7d2af39a9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb <jsnewcomb@pm.me> Date: Thu, 4 Jul 2024 19:00:17 -0400 Subject: [PATCH 21/54] Refactor `absolute_paths`: * Check the path length first * Use `is_from_proc_macro` * Use symbols instead of strings when checking crate names --- clippy_lints/src/absolute_paths.rs | 95 +++++----- clippy_utils/src/check_proc_macro.rs | 12 +- .../absolute_paths.allow_crates.stderr | 45 +++-- .../absolute_paths.allow_long.stderr | 14 ++ .../absolute_paths.default.stderr | 80 ++++++++ .../absolute_paths.disallow_crates.stderr | 71 ------- .../absolute_paths.no_short.stderr | 98 ++++++++++ .../ui-toml/absolute_paths/absolute_paths.rs | 178 ++++++++++-------- .../absolute_paths_2015.default.stderr | 14 ++ .../absolute_paths/absolute_paths_2015.rs | 16 ++ .../absolute_paths/allow_crates/clippy.toml | 3 +- .../absolute_paths/allow_long/clippy.toml | 1 + .../absolute_paths/auxiliary/helper.rs | 11 -- .../absolute_paths/default/clippy.toml | 0 .../disallow_crates/clippy.toml | 1 - .../absolute_paths/no_short/clippy.toml | 1 + 16 files changed, 415 insertions(+), 225 deletions(-) create mode 100644 tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr create mode 100644 tests/ui-toml/absolute_paths/absolute_paths.default.stderr delete mode 100644 tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr create mode 100644 tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr create mode 100644 tests/ui-toml/absolute_paths/absolute_paths_2015.default.stderr create mode 100644 tests/ui-toml/absolute_paths/absolute_paths_2015.rs create mode 100644 tests/ui-toml/absolute_paths/allow_long/clippy.toml delete mode 100644 tests/ui-toml/absolute_paths/auxiliary/helper.rs create mode 100644 tests/ui-toml/absolute_paths/default/clippy.toml delete mode 100644 tests/ui-toml/absolute_paths/disallow_crates/clippy.toml create mode 100644 tests/ui-toml/absolute_paths/no_short/clippy.toml diff --git a/clippy_lints/src/absolute_paths.rs b/clippy_lints/src/absolute_paths.rs index c0a9d888e0b0b..c72b3f1318c86 100644 --- a/clippy_lints/src/absolute_paths.rs +++ b/clippy_lints/src/absolute_paths.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::snippet_opt; +use clippy_utils::is_from_proc_macro; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; @@ -8,6 +8,7 @@ use rustc_hir::{HirId, ItemKind, Node, Path}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; +use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -24,6 +25,13 @@ declare_clippy_lint! { /// Note: One exception to this is code from macro expansion - this does not lint such cases, as /// using absolute paths is the proper way of referencing items in one. /// + /// ### Known issues + /// + /// There are currently a few cases which are not caught by this lint: + /// * Macro calls. e.g. `path::to::macro!()` + /// * Derive macros. e.g. `#[derive(path::to::macro)]` + /// * Attribute macros. e.g. `#[path::to::macro]` + /// /// ### Example /// ```no_run /// let x = std::f64::consts::PI; @@ -48,63 +56,66 @@ impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]); pub struct AbsolutePaths { pub absolute_paths_max_segments: u64, - pub absolute_paths_allowed_crates: &'static FxHashSet<String>, + pub absolute_paths_allowed_crates: FxHashSet<Symbol>, } impl AbsolutePaths { pub fn new(conf: &'static Conf) -> Self { Self { absolute_paths_max_segments: conf.absolute_paths_max_segments, - absolute_paths_allowed_crates: &conf.absolute_paths_allowed_crates, + absolute_paths_allowed_crates: conf + .absolute_paths_allowed_crates + .iter() + .map(|x| Symbol::intern(x)) + .collect(), } } } -impl LateLintPass<'_> for AbsolutePaths { +impl<'tcx> LateLintPass<'tcx> for AbsolutePaths { // We should only lint `QPath::Resolved`s, but since `Path` is only used in `Resolved` and `UsePath` // we don't need to use a visitor or anything as we can just check if the `Node` for `hir_id` isn't // a `Use` - #[expect(clippy::cast_possible_truncation)] - fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) { - let Self { - absolute_paths_max_segments, - absolute_paths_allowed_crates, - } = self; - - if !path.span.from_expansion() - && let node = cx.tcx.hir_node(hir_id) - && !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(_, _))) - && let [first, rest @ ..] = path.segments - // Handle `::std` - && let (segment, len) = if first.ident.name == kw::PathRoot { - // Indexing is fine as `PathRoot` must be followed by another segment. `len() - 1` - // is fine here for the same reason - (&rest[0], path.segments.len() - 1) - } else { - (first, path.segments.len()) - } - && len > *absolute_paths_max_segments as usize - && let Some(segment_snippet) = snippet_opt(cx, segment.ident.span) - && segment_snippet == segment.ident.as_str() - { - let is_abs_external = - matches!(segment.res, Res::Def(DefKind::Mod, DefId { index, .. }) if index == CRATE_DEF_INDEX); - let is_abs_crate = segment.ident.name == kw::Crate; - - if is_abs_external && absolute_paths_allowed_crates.contains(segment.ident.name.as_str()) - || is_abs_crate && absolute_paths_allowed_crates.contains("crate") + fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, hir_id: HirId) { + let segments = match path.segments { + [] | [_] => return, + // Don't count enum variants and trait items as part of the length. + [rest @ .., _] + if let [.., s] = rest + && matches!(s.res, Res::Def(DefKind::Enum | DefKind::Trait | DefKind::TraitAlias, _)) => + { + rest + }, + path => path, + }; + if let [s1, s2, ..] = segments + && let has_root = s1.ident.name == kw::PathRoot + && let first = if has_root { s2 } else { s1 } + && let len = segments.len() - usize::from(has_root) + && len as u64 > self.absolute_paths_max_segments + && let crate_name = if let Res::Def(DefKind::Mod, DefId { index, .. }) = first.res + && index == CRATE_DEF_INDEX { + // `other_crate::foo` or `::other_crate::foo` + first.ident.name + } else if first.ident.name == kw::Crate || has_root { + // `::foo` or `crate::foo` + kw::Crate + } else { return; } - - if is_abs_external || is_abs_crate { - span_lint( - cx, - ABSOLUTE_PATHS, - path.span, - "consider bringing this path into scope with the `use` keyword", - ); - } + && !path.span.from_expansion() + && let node = cx.tcx.hir_node(hir_id) + && !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(..))) + && !self.absolute_paths_allowed_crates.contains(&crate_name) + && !is_from_proc_macro(cx, path) + { + span_lint( + cx, + ABSOLUTE_PATHS, + path.span, + "consider bringing this path into scope with the `use` keyword", + ); } } } diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 7f2234b310b42..2bd6837d973bb 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -123,16 +123,14 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) { fn path_search_pat(path: &Path<'_>) -> (Pat, Pat) { let (head, tail) = match path.segments { - [head, .., tail] => (head, tail), - [p] => (p, p), [] => return (Pat::Str(""), Pat::Str("")), + [p] => (Pat::Sym(p.ident.name), p), + // QPath::Resolved can have a path that looks like `<Foo as Bar>::baz` where + // the path (`Bar::baz`) has it's span covering the whole QPath. + [.., tail] => (Pat::Str(""), tail), }; ( - if head.ident.name == kw::PathRoot { - Pat::Str("::") - } else { - Pat::Sym(head.ident.name) - }, + head, if tail.args.is_some() { Pat::Str(">") } else { diff --git a/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr b/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr index 1cc1034cd89b3..d24b0cd616294 100644 --- a/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr +++ b/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr @@ -1,29 +1,44 @@ error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:5 + --> tests/ui-toml/absolute_paths/absolute_paths.rs:14:13 | -LL | std::f32::MAX; - | ^^^^^^^^^^^^^ +LL | let _ = std::path::is_separator(' '); + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `-D clippy::absolute-paths` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::absolute_paths)]` +note: the lint level is defined here + --> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9 + | +LL | #![deny(clippy::absolute_paths)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:20:13 + | +LL | let _ = ::std::path::MAIN_SEPARATOR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13 + | +LL | let _ = std::collections::hash_map::HashMap::<i32, i32>::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:41:5 + --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:31 | -LL | core::f32::MAX; - | ^^^^^^^^^^^^^^ +LL | let _: &std::path::Path = std::path::Path::new(""); + | ^^^^^^^^^^^^^^^ error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:42:5 + --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:13 | -LL | ::core::f32::MAX; - | ^^^^^^^^^^^^^^^^ +LL | let _: &std::path::Path = std::path::Path::new(""); + | ^^^^^^^^^^^^^^^ error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:58:5 + --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:13 | -LL | ::std::f32::MAX; - | ^^^^^^^^^^^^^^^ +LL | let _ = std::option::Option::None::<i32>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr b/tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr new file mode 100644 index 0000000000000..0cc6566af3a96 --- /dev/null +++ b/tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr @@ -0,0 +1,14 @@ +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13 + | +LL | let _ = std::collections::hash_map::HashMap::<i32, i32>::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9 + | +LL | #![deny(clippy::absolute_paths)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/absolute_paths/absolute_paths.default.stderr b/tests/ui-toml/absolute_paths/absolute_paths.default.stderr new file mode 100644 index 0000000000000..53aa9030e0dd5 --- /dev/null +++ b/tests/ui-toml/absolute_paths/absolute_paths.default.stderr @@ -0,0 +1,80 @@ +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:14:13 + | +LL | let _ = std::path::is_separator(' '); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9 + | +LL | #![deny(clippy::absolute_paths)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:20:13 + | +LL | let _ = ::std::path::MAIN_SEPARATOR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13 + | +LL | let _ = std::collections::hash_map::HashMap::<i32, i32>::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:31 + | +LL | let _: &std::path::Path = std::path::Path::new(""); + | ^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:13 + | +LL | let _: &std::path::Path = std::path::Path::new(""); + | ^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:37:13 + | +LL | let _ = ::core::clone::Clone::clone(&0i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:13 + | +LL | let _ = <i32 as core::clone::Clone>::clone(&0i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:13 + | +LL | let _ = std::option::Option::None::<i32>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:65:17 + | +LL | impl<T: core::cmp::Eq> core::fmt::Display for X<T> + | ^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:70:18 + | +LL | where T: core::clone::Clone + | ^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:65:32 + | +LL | impl<T: core::cmp::Eq> core::fmt::Display for X<T> + | ^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:116:5 + | +LL | crate::m1::S + | ^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + diff --git a/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr b/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr deleted file mode 100644 index f342ebf6632e8..0000000000000 --- a/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr +++ /dev/null @@ -1,71 +0,0 @@ -error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:5 - | -LL | std::f32::MAX; - | ^^^^^^^^^^^^^ - | - = note: `-D clippy::absolute-paths` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::absolute_paths)]` - -error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:41:5 - | -LL | core::f32::MAX; - | ^^^^^^^^^^^^^^ - -error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:42:5 - | -LL | ::core::f32::MAX; - | ^^^^^^^^^^^^^^^^ - -error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:5 - | -LL | crate::a::b::c::C; - | ^^^^^^^^^^^^^^^^^ - -error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:44:5 - | -LL | crate::a::b::c::d::e::f::F; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:45:5 - | -LL | crate::a::A; - | ^^^^^^^^^^^ - -error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:46:5 - | -LL | crate::a::b::B; - | ^^^^^^^^^^^^^^ - -error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:47:5 - | -LL | crate::a::b::c::C::ZERO; - | ^^^^^^^^^^^^^^^^^ - -error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:48:5 - | -LL | helper::b::c::d::e::f(); - | ^^^^^^^^^^^^^^^^^^^^^ - -error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:49:5 - | -LL | ::helper::b::c::d::e::f(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - -error: consider bringing this path into scope with the `use` keyword - --> tests/ui-toml/absolute_paths/absolute_paths.rs:58:5 - | -LL | ::std::f32::MAX; - | ^^^^^^^^^^^^^^^ - -error: aborting due to 11 previous errors - diff --git a/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr b/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr new file mode 100644 index 0000000000000..70d71f6c4ea16 --- /dev/null +++ b/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr @@ -0,0 +1,98 @@ +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:14:13 + | +LL | let _ = std::path::is_separator(' '); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9 + | +LL | #![deny(clippy::absolute_paths)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:20:13 + | +LL | let _ = ::std::path::MAIN_SEPARATOR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13 + | +LL | let _ = std::collections::hash_map::HashMap::<i32, i32>::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:31 + | +LL | let _: &std::path::Path = std::path::Path::new(""); + | ^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:13 + | +LL | let _: &std::path::Path = std::path::Path::new(""); + | ^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:37:13 + | +LL | let _ = ::core::clone::Clone::clone(&0i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:13 + | +LL | let _ = <i32 as core::clone::Clone>::clone(&0i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:13 + | +LL | let _ = std::option::Option::None::<i32>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:65:17 + | +LL | impl<T: core::cmp::Eq> core::fmt::Display for X<T> + | ^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:70:18 + | +LL | where T: core::clone::Clone + | ^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:65:32 + | +LL | impl<T: core::cmp::Eq> core::fmt::Display for X<T> + | ^^^^^^^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:113:14 + | +LL | pub const _: crate::S = { + | ^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:114:9 + | +LL | let crate::S = m1::S; + | ^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:116:5 + | +LL | crate::m1::S + | ^^^^^^^^^^^^ + +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths.rs:122:14 + | +LL | let _ = <crate::S as Clone>::clone(&m1::S); + | ^^^^^^^^ + +error: aborting due to 15 previous errors + diff --git a/tests/ui-toml/absolute_paths/absolute_paths.rs b/tests/ui-toml/absolute_paths/absolute_paths.rs index a828701bcee9f..c024f2f513ced 100644 --- a/tests/ui-toml/absolute_paths/absolute_paths.rs +++ b/tests/ui-toml/absolute_paths/absolute_paths.rs @@ -1,97 +1,123 @@ //@aux-build:../../ui/auxiliary/proc_macros.rs -//@aux-build:helper.rs -//@revisions: allow_crates disallow_crates +//@revisions: default allow_crates allow_long no_short +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/default //@[allow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_crates -//@[disallow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/disallow_crates -#![allow(clippy::no_effect, clippy::legacy_numeric_constants, unused)] -#![warn(clippy::absolute_paths)] -#![feature(decl_macro)] +//@[allow_long] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_long +//@[no_short] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/no_short +#![deny(clippy::absolute_paths)] -extern crate helper; -#[macro_use] extern crate proc_macros; +use proc_macros::{external, inline_macros, with_span}; -pub mod a { - pub mod b { - pub mod c { - pub struct C; +#[inline_macros] +fn main() { + let _ = std::path::is_separator(' '); + //~[default]^ absolute_paths + //~[allow_crates]| absolute_paths + //~[no_short]| absolute_paths - impl C { - pub const ZERO: u32 = 0; - } + // Make sure this is treated as having three path segments, not four. + let _ = ::std::path::MAIN_SEPARATOR; + //~[default]^ absolute_paths + //~[allow_crates]| absolute_paths + //~[no_short]| absolute_paths - pub mod d { - pub mod e { - pub mod f { - pub struct F; - } - } - } - } + let _ = std::collections::hash_map::HashMap::<i32, i32>::new(); //~ absolute_paths - pub struct B; - } + // Note `std::path::Path::new` is treated as having three parts + let _: &std::path::Path = std::path::Path::new(""); + //~[default]^ absolute_paths + //~[default]| absolute_paths + //~[allow_crates]| absolute_paths + //~[allow_crates]| absolute_paths + //~[no_short]| absolute_paths + //~[no_short]| absolute_paths - pub struct A; -} + // Treated as having three parts. + let _ = ::core::clone::Clone::clone(&0i32); + //~[default]^ absolute_paths + //~[no_short]| absolute_paths + let _ = <i32 as core::clone::Clone>::clone(&0i32); + //~[default]^ absolute_paths + //~[no_short]| absolute_paths + let _ = std::option::Option::None::<i32>; + //~[default]^ absolute_paths + //~[allow_crates]| absolute_paths + //~[no_short]| absolute_paths -fn main() { - f32::max(1.0, 2.0); - std::f32::MAX; - core::f32::MAX; - ::core::f32::MAX; - crate::a::b::c::C; - crate::a::b::c::d::e::f::F; - crate::a::A; - crate::a::b::B; - crate::a::b::c::C::ZERO; - helper::b::c::d::e::f(); - ::helper::b::c::d::e::f(); - fn b() -> a::b::B { - todo!() + { + // FIXME: macro calls should be checked. + let x = 1i32; + let _ = core::ptr::addr_of!(x); } - std::println!("a"); - let x = 1; - std::ptr::addr_of!(x); - // Test we handle max segments with `PathRoot` properly; this has 4 segments but we should say it - // has 3 - ::std::f32::MAX; - // Do not lint due to the above - ::helper::a(); - // Do not lint - helper::a(); - use crate::a::b::c::C; - use a::b; - use std::f32::MAX; - a::b::c::d::e::f::F; - b::c::C; - fn a() -> a::A { - todo!() - } - use a::b::c; - fn c() -> c::C { - todo!() + { + // FIXME: derive macro paths should be checked. + #[derive(core::clone::Clone)] + struct S; } - fn d() -> Result<(), ()> { - todo!() + + { + use core::fmt; + use core::marker::PhantomData; + + struct X<T>(PhantomData<T>); + impl<T: core::cmp::Eq> core::fmt::Display for X<T> + //~[default]^ absolute_paths + //~[default]| absolute_paths + //~[no_short]| absolute_paths + //~[no_short]| absolute_paths + where T: core::clone::Clone + //~[no_short]^ absolute_paths + //~[default]| absolute_paths + { + fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { + Ok(()) + } + } } - external! { - crate::a::b::c::C::ZERO; + + { + mod m1 { + pub(crate) mod m2 { + pub(crate) const FOO: i32 = 0; + } + } + let _ = m1::m2::FOO; } - // For some reason, `path.span.from_expansion()` takes care of this for us + with_span! { span - crate::a::b::c::C::ZERO; + let _ = std::path::is_separator(' '); } - macro_rules! local_crate { - () => { - crate::a::b::c::C::ZERO; - }; + + external! { + let _ = std::path::is_separator(' '); } - macro local_crate_2_0() { - crate::a::b::c::C::ZERO; + + inline! { + let _ = std::path::is_separator(' '); } - local_crate!(); - local_crate_2_0!(); +} + +pub use core::cmp::Ordering; +pub use std::fs::File; + +#[derive(Clone)] +pub struct S; +mod m1 { + pub use crate::S; +} + +//~[no_short]v absolute_paths +pub const _: crate::S = { + let crate::S = m1::S; //~[no_short] absolute_paths + + crate::m1::S + //~[default]^ absolute_paths + //~[no_short]| absolute_paths +}; + +pub fn f() { + let _ = <crate::S as Clone>::clone(&m1::S); //~[no_short] absolute_paths } diff --git a/tests/ui-toml/absolute_paths/absolute_paths_2015.default.stderr b/tests/ui-toml/absolute_paths/absolute_paths_2015.default.stderr new file mode 100644 index 0000000000000..6fc495f018080 --- /dev/null +++ b/tests/ui-toml/absolute_paths/absolute_paths_2015.default.stderr @@ -0,0 +1,14 @@ +error: consider bringing this path into scope with the `use` keyword + --> tests/ui-toml/absolute_paths/absolute_paths_2015.rs:15:13 + | +LL | let _ = ::m1::m2::X; + | ^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui-toml/absolute_paths/absolute_paths_2015.rs:6:9 + | +LL | #![deny(clippy::absolute_paths)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/absolute_paths/absolute_paths_2015.rs b/tests/ui-toml/absolute_paths/absolute_paths_2015.rs new file mode 100644 index 0000000000000..033c47809191c --- /dev/null +++ b/tests/ui-toml/absolute_paths/absolute_paths_2015.rs @@ -0,0 +1,16 @@ +//@revisions: default allow_crates +//@[default]rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/default +//@[allow_crates]rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_crates +//@edition:2015 + +#![deny(clippy::absolute_paths)] + +mod m1 { + pub mod m2 { + pub struct X; + } +} + +fn main() { + let _ = ::m1::m2::X; //~[default] absolute_paths +} diff --git a/tests/ui-toml/absolute_paths/allow_crates/clippy.toml b/tests/ui-toml/absolute_paths/allow_crates/clippy.toml index 59a621e9d1dba..9da49abcd31f6 100644 --- a/tests/ui-toml/absolute_paths/allow_crates/clippy.toml +++ b/tests/ui-toml/absolute_paths/allow_crates/clippy.toml @@ -1,2 +1 @@ -absolute-paths-max-segments = 2 -absolute-paths-allowed-crates = ["crate", "helper"] +absolute-paths-allowed-crates = ["core", "crate"] diff --git a/tests/ui-toml/absolute_paths/allow_long/clippy.toml b/tests/ui-toml/absolute_paths/allow_long/clippy.toml new file mode 100644 index 0000000000000..5992fd1ed8219 --- /dev/null +++ b/tests/ui-toml/absolute_paths/allow_long/clippy.toml @@ -0,0 +1 @@ +absolute-paths-max-segments = 3 diff --git a/tests/ui-toml/absolute_paths/auxiliary/helper.rs b/tests/ui-toml/absolute_paths/auxiliary/helper.rs deleted file mode 100644 index 8e2678f5fe697..0000000000000 --- a/tests/ui-toml/absolute_paths/auxiliary/helper.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub fn a() {} - -pub mod b { - pub mod c { - pub mod d { - pub mod e { - pub fn f() {} - } - } - } -} diff --git a/tests/ui-toml/absolute_paths/default/clippy.toml b/tests/ui-toml/absolute_paths/default/clippy.toml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml b/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml deleted file mode 100644 index d44d648c64118..0000000000000 --- a/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml +++ /dev/null @@ -1 +0,0 @@ -absolute-paths-max-segments = 2 diff --git a/tests/ui-toml/absolute_paths/no_short/clippy.toml b/tests/ui-toml/absolute_paths/no_short/clippy.toml new file mode 100644 index 0000000000000..357524420c5a6 --- /dev/null +++ b/tests/ui-toml/absolute_paths/no_short/clippy.toml @@ -0,0 +1 @@ +absolute-paths-max-segments = 0 From 917ad034cc89ecdde85771238d0d8f89813290fb Mon Sep 17 00:00:00 2001 From: Nadrieril <nadrieril+git@gmail.com> Date: Wed, 20 Mar 2024 22:51:29 +0100 Subject: [PATCH 22/54] Fixes in various places --- clippy_utils/src/lib.rs | 1 + tests/ui/single_match_else.fixed | 2 +- tests/ui/single_match_else.rs | 2 +- tests/ui/single_match_else.stderr | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index af74e4b67c162..28755ae0710ca 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2931,6 +2931,7 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU moved_before_use, same_ctxt, }, + #[allow(unreachable_patterns)] Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"), None => ExprUseCtxt { node: Node::Crate(cx.tcx.hir().root_module()), diff --git a/tests/ui/single_match_else.fixed b/tests/ui/single_match_else.fixed index e840adf0fa34b..163be16ad8be7 100644 --- a/tests/ui/single_match_else.fixed +++ b/tests/ui/single_match_else.fixed @@ -89,7 +89,7 @@ fn main() { // lint here use std::convert::Infallible; - if let Ok(a) = Result::<i32, Infallible>::Ok(1) { println!("${:?}", a) } else { + if let Ok(a) = Result::<i32, &Infallible>::Ok(1) { println!("${:?}", a) } else { println!("else block"); return; } diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 430c4da20f12a..3f1fd2b31832f 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -98,7 +98,7 @@ fn main() { // lint here use std::convert::Infallible; - match Result::<i32, Infallible>::Ok(1) { + match Result::<i32, &Infallible>::Ok(1) { Ok(a) => println!("${:?}", a), Err(_) => { println!("else block"); diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index f8f88379d6d12..61c348260d05e 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -64,7 +64,7 @@ LL + } error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> tests/ui/single_match_else.rs:101:5 | -LL | / match Result::<i32, Infallible>::Ok(1) { +LL | / match Result::<i32, &Infallible>::Ok(1) { LL | | Ok(a) => println!("${:?}", a), LL | | Err(_) => { LL | | println!("else block"); @@ -75,7 +75,7 @@ LL | | } | help: try | -LL ~ if let Ok(a) = Result::<i32, Infallible>::Ok(1) { println!("${:?}", a) } else { +LL ~ if let Ok(a) = Result::<i32, &Infallible>::Ok(1) { println!("${:?}", a) } else { LL + println!("else block"); LL + return; LL + } From 4231e93bf7933aa1f2d923f9388090f63e649c2f Mon Sep 17 00:00:00 2001 From: Alex Macleod <alex@macleod.io> Date: Sat, 10 Aug 2024 10:50:47 +0000 Subject: [PATCH 23/54] Remove `find_format_arg_expr` AST fallback --- clippy_lints/src/format.rs | 2 +- clippy_lints/src/format_args.rs | 29 ++++++----------------------- clippy_lints/src/format_impl.rs | 2 +- clippy_utils/src/macros.rs | 9 ++------- 4 files changed, 10 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 0b248f784b762..c14bd38a1f21c 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { span_useless_format(cx, call_site, sugg, applicability); }, ([arg], [piece]) => { - if let Ok(value) = find_format_arg_expr(expr, arg) + if let Some(value) = find_format_arg_expr(expr, arg) && let FormatArgsPiece::Placeholder(placeholder) = piece && placeholder.format_trait == FormatTrait::Display && placeholder.format_options == FormatOptions::default() diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index a31d5cb6ec77f..823992767e508 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -224,13 +224,11 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> { if let FormatArgsPiece::Placeholder(placeholder) = piece && let Ok(index) = placeholder.argument.index && let Some(arg) = self.format_args.arguments.all_args().get(index) + && let Some(arg_expr) = find_format_arg_expr(self.expr, arg) { - let arg_expr = find_format_arg_expr(self.expr, arg); - self.check_unused_format_specifier(placeholder, arg_expr); - if let Ok(arg_expr) = arg_expr - && placeholder.format_trait == FormatTrait::Display + if placeholder.format_trait == FormatTrait::Display && placeholder.format_options == FormatOptions::default() && !self.is_aliased(index) { @@ -242,28 +240,13 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> { } } - fn check_unused_format_specifier( - &self, - placeholder: &FormatPlaceholder, - arg_expr: Result<&Expr<'_>, &rustc_ast::Expr>, - ) { - let ty_or_ast_expr = arg_expr.map(|expr| self.cx.typeck_results().expr_ty(expr).peel_refs()); - - let is_format_args = match ty_or_ast_expr { - Ok(ty) => is_type_lang_item(self.cx, ty, LangItem::FormatArguments), - Err(expr) => matches!(expr.peel_parens_and_refs().kind, rustc_ast::ExprKind::FormatArgs(_)), - }; - + fn check_unused_format_specifier(&self, placeholder: &FormatPlaceholder, arg: &Expr<'_>) { let options = &placeholder.format_options; - let arg_span = match arg_expr { - Ok(expr) => expr.span, - Err(expr) => expr.span, - }; - if let Some(placeholder_span) = placeholder.span - && is_format_args && *options != FormatOptions::default() + && let ty = self.cx.typeck_results().expr_ty(arg).peel_refs() + && is_type_lang_item(self.cx, ty, LangItem::FormatArguments) { span_lint_and_then( self.cx, @@ -274,7 +257,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> { let mut suggest_format = |spec| { let message = format!("for the {spec} to apply consider using `format!()`"); - if let Some(mac_call) = matching_root_macro_call(self.cx, arg_span, sym::format_args_macro) { + if let Some(mac_call) = matching_root_macro_call(self.cx, arg.span, sym::format_args_macro) { diag.span_suggestion( self.cx.sess().source_map().span_until_char(mac_call.span, '!'), message, diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index e6f27cb82d17a..7cd12ac7db852 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -196,7 +196,7 @@ impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> { && trait_name == self.format_trait_impl.name && let Ok(index) = placeholder.argument.index && let Some(arg) = format_args.arguments.all_args().get(index) - && let Ok(arg_expr) = find_format_arg_expr(self.expr, arg) + && let Some(arg_expr) = find_format_arg_expr(self.expr, arg) { self.check_format_arg_self(arg_expr); } diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 455239cc37f35..4a635535a4e0c 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -426,12 +426,8 @@ impl FormatArgsStorage { } } -/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if -/// it cannot be found it will return the [`rustc_ast::Expr`]. -pub fn find_format_arg_expr<'hir, 'ast>( - start: &'hir Expr<'hir>, - target: &'ast FormatArgument, -) -> Result<&'hir Expr<'hir>, &'ast rustc_ast::Expr> { +/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value +pub fn find_format_arg_expr<'hir>(start: &'hir Expr<'hir>, target: &FormatArgument) -> Option<&'hir Expr<'hir>> { let SpanData { lo, hi, @@ -449,7 +445,6 @@ pub fn find_format_arg_expr<'hir, 'ast>( ControlFlow::Continue(()) } }) - .ok_or(&target.expr) } /// Span of the `:` and format specifiers From 6de27a027223b3fed221d256ed6e8a9abf4b6b51 Mon Sep 17 00:00:00 2001 From: Alex Macleod <alex@macleod.io> Date: Sat, 10 Aug 2024 18:36:52 +0000 Subject: [PATCH 24/54] lintcheck: key lints on line start rather than byte start/end --- lintcheck/src/json.rs | 39 +++++++++++++++++++++------------------ lintcheck/src/output.rs | 14 +++++++------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/lintcheck/src/json.rs b/lintcheck/src/json.rs index 4211bce90b2fb..ee0c80aea522e 100644 --- a/lintcheck/src/json.rs +++ b/lintcheck/src/json.rs @@ -12,21 +12,21 @@ const TRUNCATION_TOTAL_TARGET: usize = 1000; #[derive(Debug, Deserialize, Serialize)] struct LintJson { - lint: String, - krate: String, - file_name: String, - byte_pos: (u32, u32), - file_link: String, + /// The lint name e.g. `clippy::bytes_nth` + name: String, + /// The filename and line number e.g. `anyhow-1.0.86/src/error.rs:42` + file_line: String, + file_url: String, rendered: String, } impl LintJson { fn key(&self) -> impl Ord + '_ { - (self.lint.as_str(), self.file_name.as_str(), self.byte_pos) + (self.name.as_str(), self.file_line.as_str()) } fn info_text(&self, action: &str) -> String { - format!("{action} `{}` in `{}` at {}", self.lint, self.krate, self.file_link) + format!("{action} `{}` at [`{}`]({})", self.name, self.file_line, self.file_url) } } @@ -36,13 +36,16 @@ pub(crate) fn output(clippy_warnings: Vec<ClippyWarning>) -> String { .into_iter() .map(|warning| { let span = warning.span(); + let file_name = span + .file_name + .strip_prefix("target/lintcheck/sources/") + .unwrap_or(&span.file_name); + let file_line = format!("{file_name}:{}", span.line_start); LintJson { - file_name: span.file_name.clone(), - byte_pos: (span.byte_start, span.byte_end), - krate: warning.krate, - file_link: warning.url, - lint: warning.lint, - rendered: warning.diag.rendered.unwrap(), + name: warning.name, + file_line, + file_url: warning.url, + rendered: warning.diag.rendered.unwrap().trim().to_string(), } }) .collect(); @@ -63,7 +66,7 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { let mut lint_warnings = vec![]; for (name, changes) in &itertools::merge_join_by(old_warnings, new_warnings, |old, new| old.key().cmp(&new.key())) - .chunk_by(|change| change.as_ref().into_left().lint.to_string()) + .chunk_by(|change| change.as_ref().into_left().name.clone()) { let mut added = Vec::new(); let mut removed = Vec::new(); @@ -162,7 +165,7 @@ fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) { return; } - print_h3(&warnings[0].lint, title); + print_h3(&warnings[0].name, title); println!(); let warnings = truncate(warnings, truncate_after); @@ -171,7 +174,7 @@ fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) { println!("{}", warning.info_text(title)); println!(); println!("```"); - println!("{}", warning.rendered.trim_end()); + println!("{}", warning.rendered); println!("```"); println!(); } @@ -182,7 +185,7 @@ fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) { return; } - print_h3(&changed[0].0.lint, "Changed"); + print_h3(&changed[0].0.name, "Changed"); println!(); let changed = truncate(changed, truncate_after); @@ -191,7 +194,7 @@ fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) { println!("{}", new.info_text("Changed")); println!(); println!("```diff"); - for change in diff::lines(old.rendered.trim_end(), new.rendered.trim_end()) { + for change in diff::lines(&old.rendered, &new.rendered) { use diff::Result::{Both, Left, Right}; match change { diff --git a/lintcheck/src/output.rs b/lintcheck/src/output.rs index 153786306956a..e38036315c2ce 100644 --- a/lintcheck/src/output.rs +++ b/lintcheck/src/output.rs @@ -51,7 +51,7 @@ impl RustcIce { /// A single warning that clippy issued while checking a `Crate` #[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ClippyWarning { - pub lint: String, + pub name: String, pub diag: Diagnostic, pub krate: String, /// The URL that points to the file and line of the lint emission @@ -60,8 +60,8 @@ pub struct ClippyWarning { impl ClippyWarning { pub fn new(mut diag: Diagnostic, base_url: &str, krate: &str) -> Option<Self> { - let lint = diag.code.clone()?.code; - if !(lint.contains("clippy") || diag.message.contains("clippy")) + let name = diag.code.clone()?.code; + if !(name.contains("clippy") || diag.message.contains("clippy")) || diag.message.contains("could not read cargo metadata") { return None; @@ -92,7 +92,7 @@ impl ClippyWarning { }; Some(Self { - lint, + name, diag, url, krate: krate.to_string(), @@ -108,7 +108,7 @@ impl ClippyWarning { let mut file = span.file_name.clone(); let file_with_pos = format!("{file}:{}:{}", span.line_start, span.line_end); match format { - OutputFormat::Text => format!("{file_with_pos} {} \"{}\"\n", self.lint, self.diag.message), + OutputFormat::Text => format!("{file_with_pos} {} \"{}\"\n", self.name, self.diag.message), OutputFormat::Markdown => { if file.starts_with("target") { file.insert_str(0, "../"); @@ -116,7 +116,7 @@ impl ClippyWarning { let mut output = String::from("| "); write!(output, "[`{file_with_pos}`]({file}#L{})", span.line_start).unwrap(); - write!(output, r#" | `{:<50}` | "{}" |"#, self.lint, self.diag.message).unwrap(); + write!(output, r#" | `{:<50}` | "{}" |"#, self.name, self.diag.message).unwrap(); output.push('\n'); output }, @@ -164,7 +164,7 @@ fn gather_stats(warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) let mut counter: HashMap<&String, usize> = HashMap::new(); warnings .iter() - .for_each(|wrn| *counter.entry(&wrn.lint).or_insert(0) += 1); + .for_each(|wrn| *counter.entry(&wrn.name).or_insert(0) += 1); // collect into a tupled list for sorting let mut stats: Vec<(&&String, &usize)> = counter.iter().collect(); From 837d9045c5cf40b2ebf4b4ae2a02f35f8c7e0825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= <john.kare.alsaker@gmail.com> Date: Tue, 12 Mar 2024 02:12:28 +0100 Subject: [PATCH 25/54] Link `std` statically in `rustc_driver` --- src/main.rs | 3 +++ tests/compile-test.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/main.rs b/src/main.rs index c9af2138a7233..c9853e53f3b38 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ +// We need this feature as it changes `dylib` linking behavior and allows us to link to +// `rustc_driver`. +#![feature(rustc_private)] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] diff --git a/tests/compile-test.rs b/tests/compile-test.rs index c7080e5dcdc9e..64253514fbe67 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,3 +1,6 @@ +// We need this feature as it changes `dylib` linking behavior and allows us to link to +// `rustc_driver`. +#![feature(rustc_private)] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] From f93fb9a41716b0aae160900999170dc1a83e670b Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk <alexsemenyuk88@gmail.com> Date: Sun, 11 Aug 2024 20:33:28 +0500 Subject: [PATCH 26/54] Clarify message for unwrap lint --- clippy_lints/src/unwrap.rs | 6 ++--- .../complex_conditionals_nested.stderr | 2 +- .../checked_unwrap/simple_conditionals.stderr | 26 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index c0d9bcdd259c7..0b4ea0752b6ba 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -89,15 +89,15 @@ enum UnwrappableKind { impl UnwrappableKind { fn success_variant_pattern(self) -> &'static str { match self { - UnwrappableKind::Option => "Some(..)", - UnwrappableKind::Result => "Ok(..)", + UnwrappableKind::Option => "Some(<item>)", + UnwrappableKind::Result => "Ok(<item>)", } } fn error_variant_pattern(self) -> &'static str { match self { UnwrappableKind::Option => "None", - UnwrappableKind::Result => "Err(..)", + UnwrappableKind::Result => "Err(<item>)", } } } diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr index f7e659b10de07..329be4d366210 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -2,7 +2,7 @@ error: called `unwrap` on `x` after checking its variant with `is_some` --> tests/ui/checked_unwrap/complex_conditionals_nested.rs:13:13 | LL | if x.is_some() { - | -------------- help: try: `if let Some(..) = x` + | -------------- help: try: `if let Some(<item>) = x` LL | // unnecessary LL | x.unwrap(); | ^^^^^^^^^^ diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index ddd600418afec..f7e338935a770 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -2,7 +2,7 @@ error: called `unwrap` on `x` after checking its variant with `is_some` --> tests/ui/checked_unwrap/simple_conditionals.rs:46:9 | LL | if x.is_some() { - | -------------- help: try: `if let Some(..) = x` + | -------------- help: try: `if let Some(<item>) = x` LL | // unnecessary LL | x.unwrap(); | ^^^^^^^^^^ @@ -17,7 +17,7 @@ error: called `expect` on `x` after checking its variant with `is_some` --> tests/ui/checked_unwrap/simple_conditionals.rs:49:9 | LL | if x.is_some() { - | -------------- help: try: `if let Some(..) = x` + | -------------- help: try: `if let Some(<item>) = x` ... LL | x.expect("an error message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -59,7 +59,7 @@ error: called `unwrap` on `x` after checking its variant with `is_none` --> tests/ui/checked_unwrap/simple_conditionals.rs:65:9 | LL | if x.is_none() { - | -------------- help: try: `if let Some(..) = x` + | -------------- help: try: `if let Some(<item>) = x` ... LL | x.unwrap(); | ^^^^^^^^^^ @@ -68,7 +68,7 @@ error: called `unwrap` on `x` after checking its variant with `is_some` --> tests/ui/checked_unwrap/simple_conditionals.rs:13:13 | LL | if $a.is_some() { - | --------------- help: try: `if let Some(..) = x` + | --------------- help: try: `if let Some(<item>) = x` LL | // unnecessary LL | $a.unwrap(); | ^^^^^^^^^^^ @@ -82,7 +82,7 @@ error: called `unwrap` on `x` after checking its variant with `is_ok` --> tests/ui/checked_unwrap/simple_conditionals.rs:78:9 | LL | if x.is_ok() { - | ------------ help: try: `if let Ok(..) = x` + | ------------ help: try: `if let Ok(<item>) = x` LL | // unnecessary LL | x.unwrap(); | ^^^^^^^^^^ @@ -91,7 +91,7 @@ error: called `expect` on `x` after checking its variant with `is_ok` --> tests/ui/checked_unwrap/simple_conditionals.rs:81:9 | LL | if x.is_ok() { - | ------------ help: try: `if let Ok(..) = x` + | ------------ help: try: `if let Ok(<item>) = x` ... LL | x.expect("an error message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -127,7 +127,7 @@ error: called `unwrap_err` on `x` after checking its variant with `is_ok` --> tests/ui/checked_unwrap/simple_conditionals.rs:94:9 | LL | if x.is_ok() { - | ------------ help: try: `if let Err(..) = x` + | ------------ help: try: `if let Err(<item>) = x` ... LL | x.unwrap_err(); | ^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ error: called `unwrap_err` on `x` after checking its variant with `is_err` --> tests/ui/checked_unwrap/simple_conditionals.rs:102:9 | LL | if x.is_err() { - | ------------- help: try: `if let Err(..) = x` + | ------------- help: try: `if let Err(<item>) = x` ... LL | x.unwrap_err(); | ^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ error: called `unwrap` on `x` after checking its variant with `is_err` --> tests/ui/checked_unwrap/simple_conditionals.rs:106:9 | LL | if x.is_err() { - | ------------- help: try: `if let Ok(..) = x` + | ------------- help: try: `if let Ok(<item>) = x` ... LL | x.unwrap(); | ^^^^^^^^^^ @@ -172,7 +172,7 @@ error: called `unwrap` on `option` after checking its variant with `is_some` --> tests/ui/checked_unwrap/simple_conditionals.rs:134:9 | LL | if option.is_some() { - | ------------------- help: try: `if let Some(..) = &option` + | ------------------- help: try: `if let Some(<item>) = &option` LL | option.as_ref().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ error: called `unwrap` on `result` after checking its variant with `is_ok` --> tests/ui/checked_unwrap/simple_conditionals.rs:144:9 | LL | if result.is_ok() { - | ----------------- help: try: `if let Ok(..) = &result` + | ----------------- help: try: `if let Ok(<item>) = &result` LL | result.as_ref().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,7 +206,7 @@ error: called `unwrap` on `option` after checking its variant with `is_some` --> tests/ui/checked_unwrap/simple_conditionals.rs:153:9 | LL | if option.is_some() { - | ------------------- help: try: `if let Some(..) = &mut option` + | ------------------- help: try: `if let Some(<item>) = &mut option` LL | option.as_mut().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -223,7 +223,7 @@ error: called `unwrap` on `result` after checking its variant with `is_ok` --> tests/ui/checked_unwrap/simple_conditionals.rs:162:9 | LL | if result.is_ok() { - | ----------------- help: try: `if let Ok(..) = &mut result` + | ----------------- help: try: `if let Ok(<item>) = &mut result` LL | result.as_mut().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ From 934e98d59135b64647e9756dafbd2151d67dd234 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Tue, 6 Aug 2024 21:38:14 +0200 Subject: [PATCH 27/54] Unify theme and settings menus --- util/gh-pages/index.html | 30 ++++--------- util/gh-pages/script.js | 93 +++++++++++++++++++--------------------- util/gh-pages/style.css | 47 ++++++++------------ 3 files changed, 72 insertions(+), 98 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 12c9608f6f5f7..845e37feaddd3 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -26,27 +26,15 @@ <link rel="stylesheet" href="style.css"> </head> <body ng-app="clippy" ng-controller="lintList"> - <div id="settings"> - <div theme-dropdown class="theme-dropdown"> - <div class="menu-container"> - <div id="theme-icon" class="theme-icon">🖌</div> - <ul id="theme-menu" class="theme-choice"> - <li id="{{id}}" ng-repeat="(id, name) in themes" ng-click="selectTheme(id)">{{name}}</li> - </ul> - </div> - </div> - <div settings-dropdown class="settings-dropdown"> - <div class="menu-container"> - <div id="settings-icon" class="settings-icon"></div> - <ul id="settings-menu" class="settings-choice"> - <li> - <label> - <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)"> - <span>Disable keyboard shortcuts</span> - </label> - </li> - </ul> - </div> + <div id="settings-dropdown"> + <div class="settings-icon" tabindex="-1"></div> + <div class="settings-menu" tabindex="-1"> + <div class="setting-radio-name">Theme</div> + <select id="theme-choice" onchange="setTheme(this.value, true)"></select> + <label> + <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)"> + <span>Disable keyboard shortcuts</span> + </label> </div> </div> diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index f072327bc340f..fc643309db366 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -50,42 +50,6 @@ ); }; }) - .directive('themeDropdown', function ($document) { - return { - restrict: 'A', - link: function ($scope, $element, $attr) { - $element.bind('click', function () { - $element.toggleClass('open'); - $element.addClass('open-recent'); - }); - - $document.bind('click', function () { - if (!$element.hasClass('open-recent')) { - $element.removeClass('open'); - } - $element.removeClass('open-recent'); - }) - } - } - }) - .directive('settingsDropdown', function ($document) { - return { - restrict: 'A', - link: function ($scope, $element, $attr) { - $element.bind('click', function () { - $element.toggleClass('open'); - $element.addClass('open-recent'); - }); - - $document.bind('click', function () { - if (!$element.hasClass('open-recent')) { - $element.removeClass('open'); - } - $element.removeClass('open-recent'); - }) - } - } - }) .directive('filterDropdown', function ($document) { return { restrict: 'A', @@ -145,15 +109,6 @@ ...GROUPS_FILTER_DEFAULT }; - const THEMES_DEFAULT = { - light: "Light", - rust: "Rust", - coal: "Coal", - navy: "Navy", - ayu: "Ayu" - }; - $scope.themes = THEMES_DEFAULT; - $scope.versionFilters = { "≥": {enabled: false, minorVersion: null }, "≤": {enabled: false, minorVersion: null }, @@ -339,10 +294,6 @@ $location.path($scope.search); } - $scope.selectTheme = function (theme) { - setTheme(theme, true); - } - $scope.toggleLevels = function (value) { const levels = $scope.levels; for (const key in levels) { @@ -598,6 +549,8 @@ function setTheme(theme, store) { if (store) { storeValue("theme", theme); + } else { + document.getElementById(`theme-choice`).value = theme; } } @@ -634,6 +587,48 @@ function changeSetting(elem) { } } +function onEachLazy(lazyArray, func) { + const arr = Array.prototype.slice.call(lazyArray); + for (const el of arr) { + func(el); + } +} + +function handleBlur(event) { + const parent = document.getElementById("settings-dropdown"); + if (!parent.contains(document.activeElement) && + !parent.contains(event.relatedTarget) + ) { + parent.classList.remove("open"); + } +} + +function generateSettings() { + const THEMES = ["Ayu", "Coal", "Light", "Navy", "Rust"]; + const themesElem = document.getElementById("theme-choice"); + let children = ''; + + for (const theme of THEMES) { + const id = theme.toLowerCase(); + children += `<option value="${id}">${theme}</option>`; + } + themesElem.innerHTML = children; + themesElem.onblur = handleBlur; + + const settings = document.getElementById("settings-dropdown"); + const settingsButton = settings.querySelector(".settings-icon") + settingsButton.onclick = () => settings.classList.toggle("open"); + settingsButton.onblur = handleBlur; + const settingsMenu = settings.querySelector(".settings-menu"); + settingsMenu.onblur = handleBlur; + onEachLazy( + settingsMenu.querySelectorAll("input"), + el => el.onblur = handleBlur, + ); +} + +generateSettings(); + // loading the theme after the initial load const prefersDark = window.matchMedia("(prefers-color-scheme: dark)"); const theme = loadValue('theme'); diff --git a/util/gh-pages/style.css b/util/gh-pages/style.css index 4ad8b502dd82f..a9485d511047a 100644 --- a/util/gh-pages/style.css +++ b/util/gh-pages/style.css @@ -220,20 +220,15 @@ details[open] { --inline-code-bg: #191f26; } -#settings { +#settings-dropdown { position: absolute; margin: 0.7em; z-index: 10; display: flex; } -.menu-container { - position: relative; - width: 28px; -} - /* Applying the mdBook theme */ -.theme-icon, .settings-icon { +.settings-icon { text-align: center; width: 2em; height: 2em; @@ -242,24 +237,20 @@ details[open] { border-radius: 5px; user-select: none; cursor: pointer; -} -.theme-icon:hover, .settings-icon:hover { background: var(--theme-hover); } -.theme-choice, .settings-choice { +.settings-menu { display: none; list-style: none; border: 1px solid var(--theme-popup-border); border-radius: 5px; color: var(--fg); background: var(--theme-popup-bg); - padding: 0 0; overflow: hidden; + padding: 9px; + width: 207px; position: absolute; -} - -.settings-dropdown { - margin-left: 4px; + top: 28px; } .settings-icon::before { @@ -285,28 +276,28 @@ L4.75,12h2.5l0.5393066-2.1572876 c0.2276001-0.1062012,0.4459839-0.2269287,0.649 padding-top: 3px; } -.settings-choice { - padding: 4px; - width: 212px; +.settings-menu * { + font-weight: normal; } -.settings-choice label { +.settings-menu label { cursor: pointer; } -.theme-dropdown.open .theme-choice, .settings-dropdown.open .settings-choice { +#settings-dropdown.open .settings-menu { display: block; } -.theme-choice > li { - padding: 5px 10px; - font-size: 0.8em; - user-select: none; +#theme-choice { + margin-bottom: 10px; + background: var(--searchbar-bg); + color: var(--searchbar-fg); + border-color: var(--theme-popup-border); + border-radius: 5px; cursor: pointer; -} - -.theme-choice > li:hover { - background: var(--theme-hover); + width: 100%; + border-width: 1px; + padding: 5px; } .alert { From 2ae6a4954b21e26bfc3d612c8e4dd8b23dc483a1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Sun, 11 Aug 2024 19:44:14 +0200 Subject: [PATCH 28/54] Move themes directly into HTML --- util/gh-pages/index.html | 8 +++++++- util/gh-pages/script.js | 11 ----------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 845e37feaddd3..37f964c226c99 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -30,7 +30,13 @@ <div class="settings-icon" tabindex="-1"></div> <div class="settings-menu" tabindex="-1"> <div class="setting-radio-name">Theme</div> - <select id="theme-choice" onchange="setTheme(this.value, true)"></select> + <select id="theme-choice" onchange="setTheme(this.value, true)"> + <option value="ayu">Ayu</option> + <option value="coal">Coal</option> + <option value="light">Light</option> + <option value="navy">Navy</option> + <option value="rust">Rust</option> + </select> <label> <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)"> <span>Disable keyboard shortcuts</span> diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index fc643309db366..d54b5e851c7c0 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -604,17 +604,6 @@ function handleBlur(event) { } function generateSettings() { - const THEMES = ["Ayu", "Coal", "Light", "Navy", "Rust"]; - const themesElem = document.getElementById("theme-choice"); - let children = ''; - - for (const theme of THEMES) { - const id = theme.toLowerCase(); - children += `<option value="${id}">${theme}</option>`; - } - themesElem.innerHTML = children; - themesElem.onblur = handleBlur; - const settings = document.getElementById("settings-dropdown"); const settingsButton = settings.querySelector(".settings-icon") settingsButton.onclick = () => settings.classList.toggle("open"); From ddf2ba58860b5858a8e57e39595133b2509e955a Mon Sep 17 00:00:00 2001 From: Jason Newcomb <jsnewcomb@pm.me> Date: Sun, 11 Aug 2024 17:59:35 -0400 Subject: [PATCH 29/54] Remove more instances of `snippet_opt`. --- clippy_lints/src/attrs/empty_line_after.rs | 4 +-- clippy_lints/src/attrs/non_minimal_cfg.rs | 11 +++++--- .../src/attrs/unnecessary_clippy_cfg.rs | 11 +++----- clippy_lints/src/attrs/useless_attribute.rs | 10 ++++---- clippy_lints/src/casts/as_ptr_cast_mut.rs | 4 +-- clippy_lints/src/manual_async_fn.rs | 8 +++--- clippy_lints/src/needless_pass_by_value.rs | 10 ++++---- clippy_lints/src/pathbuf_init_then_push.rs | 8 +++--- clippy_lints/src/ptr.rs | 14 ++++++----- clippy_lints/src/ptr_offset_with_cast.rs | 6 ++--- clippy_lints/src/redundant_clone.rs | 4 +-- clippy_lints/src/reference.rs | 10 ++++---- clippy_lints/src/regex.rs | 4 +-- clippy_lints/src/returns.rs | 25 +++++++++++-------- clippy_lints/src/single_range_in_vec_init.rs | 8 +++--- clippy_lints/src/trait_bounds.rs | 10 +++----- clippy_lints/src/unused_unit.rs | 22 ++++++++-------- clippy_lints/src/visibility.rs | 14 +++++------ clippy_lints/src/write.rs | 8 +++--- clippy_utils/src/source.rs | 11 +++++++- 20 files changed, 109 insertions(+), 93 deletions(-) diff --git a/clippy_lints/src/attrs/empty_line_after.rs b/clippy_lints/src/attrs/empty_line_after.rs index ca43e76ac5780..7ff644b4c445f 100644 --- a/clippy_lints/src/attrs/empty_line_after.rs +++ b/clippy_lints/src/attrs/empty_line_after.rs @@ -1,6 +1,6 @@ use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::{is_present_in_source, snippet_opt, without_block_comments}; +use clippy_utils::source::{is_present_in_source, without_block_comments, SpanRangeExt}; use rustc_ast::{AttrKind, AttrStyle}; use rustc_lint::EarlyContext; use rustc_span::Span; @@ -26,7 +26,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { item.span.parent(), ); - if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) { + if let Some(snippet) = end_of_attr_to_next_attr_or_item.get_source_text(cx) { let lines = snippet.split('\n').collect::<Vec<_>>(); let lines = without_block_comments(lines); diff --git a/clippy_lints/src/attrs/non_minimal_cfg.rs b/clippy_lints/src/attrs/non_minimal_cfg.rs index 3fde70615853d..877025cce4c2b 100644 --- a/clippy_lints/src/attrs/non_minimal_cfg.rs +++ b/clippy_lints/src/attrs/non_minimal_cfg.rs @@ -1,6 +1,6 @@ use super::{Attribute, NON_MINIMAL_CFG}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; @@ -29,8 +29,13 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) { meta.span, "unneeded sub `cfg` when there is only one condition", |diag| { - if let Some(snippet) = snippet_opt(cx, list[0].span()) { - diag.span_suggestion(meta.span, "try", snippet, Applicability::MaybeIncorrect); + if let Some(snippet) = list[0].span().get_source_text(cx) { + diag.span_suggestion( + meta.span, + "try", + snippet.to_owned(), + Applicability::MaybeIncorrect, + ); } }, ); diff --git a/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs index 486e7c6ec4f4a..478ba7a187be5 100644 --- a/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs +++ b/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -1,6 +1,7 @@ use super::{Attribute, UNNECESSARY_CLIPPY_CFG}; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; +use itertools::Itertools; use rustc_ast::AttrStyle; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, Level}; @@ -31,7 +32,7 @@ pub(super) fn check( return; } if nb_items == clippy_lints.len() { - if let Some(snippet) = snippet_opt(cx, behind_cfg_attr.span) { + if let Some(snippet) = behind_cfg_attr.span.get_source_text(cx) { span_lint_and_sugg( cx, UNNECESSARY_CLIPPY_CFG, @@ -47,11 +48,7 @@ pub(super) fn check( ); } } else { - let snippet = clippy_lints - .iter() - .filter_map(|sp| snippet_opt(cx, *sp)) - .collect::<Vec<_>>() - .join(","); + let snippet = clippy_lints.iter().filter_map(|sp| sp.get_source_text(cx)).join(","); span_lint_and_note( cx, UNNECESSARY_CLIPPY_CFG, diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index f0868231d01a3..67ba605a59fa5 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -1,7 +1,7 @@ use super::utils::{extract_clippy_lint, is_lint_level, is_word}; use super::{Attribute, USELESS_ATTRIBUTE}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{first_line_of_span, snippet_opt}; +use clippy_utils::source::{first_line_of_span, SpanRangeExt}; use rustc_ast::NestedMetaItem; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; @@ -69,14 +69,14 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) } let line_span = first_line_of_span(cx, attr.span); - if let Some(mut sugg) = snippet_opt(cx, line_span) { - if sugg.contains("#[") { + if let Some(src) = line_span.get_source_text(cx) { + if src.contains("#[") { + #[expect(clippy::collapsible_span_lint_calls)] span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| { - sugg = sugg.replacen("#[", "#![", 1); diag.span_suggestion( line_span, "if you just forgot a `!`, use", - sugg, + src.replacen("#[", "#![", 1), Applicability::MaybeIncorrect, ); }); diff --git a/clippy_lints/src/casts/as_ptr_cast_mut.rs b/clippy_lints/src/casts/as_ptr_cast_mut.rs index f05fd3fcde500..15ecba20a0bc4 100644 --- a/clippy_lints/src/casts/as_ptr_cast_mut.rs +++ b/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity() && 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) + && let Some(recv) = receiver.span.get_source_text(cx) { // `as_mut_ptr` might not exist let applicability = Applicability::MaybeIncorrect; diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 25c7e5d38b319..61723aec59049 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; +use clippy_utils::source::{position_before_rarrow, snippet_block, SpanRangeExt}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -68,8 +68,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { header_span, "this function can be simplified using the `async fn` syntax", |diag| { - if let Some(vis_snip) = snippet_opt(cx, *vis_span) - && let Some(header_snip) = snippet_opt(cx, header_span) + if let Some(vis_snip) = vis_span.get_source_text(cx) + && let Some(header_snip) = header_span.get_source_text(cx) && let Some(ret_pos) = position_before_rarrow(&header_snip) && let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output) { @@ -190,6 +190,6 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, Some((sugg, String::new())) } else { let sugg = "return the output of the future directly"; - snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {snip}"))) + output.span.get_source_text(cx).map(|src| (sugg, format!(" -> {src}"))) } } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index a0bbf6b14b217..addb4b1aee807 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::span_lint_and_then; use clippy_utils::is_self; use clippy_utils::ptr::get_spans; -use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::source::{snippet, SpanRangeExt}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; @@ -242,8 +242,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - snippet_opt(cx, span) - .map_or("change the call to".into(), |x| format!("change `{x}` to")), + span.get_source_text(cx) + .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")), suggestion, Applicability::Unspecified, ); @@ -267,8 +267,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - snippet_opt(cx, span) - .map_or("change the call to".into(), |x| format!("change `{x}` to")), + span.get_source_text(cx) + .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")), suggestion, Applicability::Unspecified, ); diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index 18bfb588a1187..d7fa48c1e38dd 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::path_to_local_id; -use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::source::{snippet, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; @@ -74,7 +74,7 @@ impl<'tcx> PathbufPushSearcher<'tcx> { && let Some(arg) = self.arg && let ExprKind::Lit(x) = arg.kind && let LitKind::Str(_, StrStyle::Cooked) = x.node - && let Some(s) = snippet_opt(cx, arg.span) + && let Some(s) = arg.span.get_source_text(cx) { Some(format!(" = PathBuf::from({s});")) } else { @@ -84,8 +84,8 @@ impl<'tcx> PathbufPushSearcher<'tcx> { fn gen_pathbuf_join(&self, cx: &LateContext<'_>) -> Option<String> { let arg = self.arg?; - let arg_str = snippet_opt(cx, arg.span)?; - let init_val = snippet_opt(cx, self.init_val.span)?; + let arg_str = arg.span.get_source_text(cx)?; + let init_val = self.init_val.span.get_source_text(cx)?; Some(format!(" = {init_val}.join({arg_str});")) } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 02c05e0aaf9ce..1e824681fa12f 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -1,7 +1,7 @@ //! Checks for usage of `&Vec[_]` and `&String`. use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::expr_sig; use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local}; @@ -243,7 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { .chain(result.replacements.iter().map(|r| { ( r.expr_span, - format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement), + format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement), ) })) .collect(), @@ -372,7 +372,7 @@ impl fmt::Display for DerefTyDisplay<'_, '_> { DerefTy::Path => f.write_str("Path"), DerefTy::Slice(hir_ty, ty) => { f.write_char('[')?; - match hir_ty.and_then(|s| snippet_opt(self.0, s)) { + match hir_ty.and_then(|s| s.get_source_text(self.0)) { Some(s) => f.write_str(&s)?, None => ty.fmt(f)?, } @@ -413,6 +413,7 @@ impl<'tcx> DerefTy<'tcx> { } } +#[expect(clippy::too_many_lines)] fn check_fn_args<'cx, 'tcx: 'cx>( cx: &'cx LateContext<'tcx>, fn_sig: ty::FnSig<'tcx>, @@ -488,8 +489,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>( return None; } - let ty_name = snippet_opt(cx, ty.span()).unwrap_or_else(|| args.type_at(1).to_string()); - span_lint_hir_and_then( cx, PTR_ARG, @@ -500,7 +499,10 @@ fn check_fn_args<'cx, 'tcx: 'cx>( diag.span_suggestion( hir_ty.span, "change this to", - format!("&{}{ty_name}", mutability.prefix_str()), + match ty.span().get_source_text(cx) { + Some(s) => format!("&{}{s}", mutability.prefix_str()), + None => format!("&{}{}", mutability.prefix_str(), args.type_at(1)), + }, Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 7c82895d60919..87a52cb2186c5 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -120,8 +120,8 @@ fn build_suggestion( receiver_expr: &Expr<'_>, cast_lhs_expr: &Expr<'_>, ) -> Option<String> { - let receiver = snippet_opt(cx, receiver_expr.span)?; - let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?; + let receiver = receiver_expr.span.get_source_text(cx)?; + let cast_lhs = cast_lhs_expr.span.get_source_text(cx)?; Some(format!("{receiver}.{}({cast_lhs})", method.suggestion())) } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index a1231c082e68a..bfdc1cbeed7e1 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,6 +1,6 @@ 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::source::SpanRangeExt; 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 rustc_errors::Applicability; @@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { .assert_crate_local() .lint_root; - if let Some(snip) = snippet_opt(cx, span) + if let Some(snip) = span.get_source_text(cx) && let Some(dot) = snip.rfind('.') { let sugg_span = span.with_lo(span.lo() + BytePos(u32::try_from(dot).unwrap())); diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 8f32cf5f2a1db..2b4ef21fc48e1 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use clippy_utils::source::{snippet_with_applicability, SpanRangeExt}; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::BytePos; +use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -56,11 +56,11 @@ impl EarlyLintPass for DerefAddrOf { { let mut applicability = Applicability::MachineApplicable; let sugg = if e.span.from_expansion() { - if let Some(macro_source) = snippet_opt(cx, e.span) { + if let Some(macro_source) = e.span.get_source_text(cx) { // Remove leading whitespace from the given span // e.g: ` $visitor` turns into `$visitor` - let trim_leading_whitespaces = |span| { - snippet_opt(cx, span) + let trim_leading_whitespaces = |span: Span| { + span.get_source_text(cx) .and_then(|snip| { #[expect(clippy::cast_possible_truncation)] snip.find(|c: char| !c.is_whitespace()) diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 95014b230431c..f6ef02b7c2330 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::{def_path_def_ids, path_def_id, paths}; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; @@ -122,7 +122,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescape }; if let Some((primary, auxiliary, kind)) = parts - && let Some(literal_snippet) = snippet_opt(cx, base) + && let Some(literal_snippet) = base.get_source_text(cx) && let Some(inner) = literal_snippet.get(offset as usize..) // Only convert to native rustc spans if the parsed regex matches the // source snippet exactly, to ensure the span offsets are correct diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 13016cdadb072..ffc9475cf9cdf 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::source::{snippet_opt, snippet_with_context}; +use clippy_utils::source::{snippet_with_context, SpanRangeExt}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::{ @@ -250,20 +250,25 @@ impl<'tcx> LateLintPass<'tcx> for Return { |err| { err.span_label(local.span, "unnecessary `let` binding"); - if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { - if binary_expr_needs_parentheses(initexpr) { - if !has_enclosing_paren(&snippet) { - snippet = format!("({snippet})"); + if let Some(src) = initexpr.span.get_source_text(cx) { + let sugg = if binary_expr_needs_parentheses(initexpr) { + if has_enclosing_paren(&src) { + src.to_owned() + } else { + format!("({src})") } } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { - if !has_enclosing_paren(&snippet) { - snippet = format!("({snippet})"); + if has_enclosing_paren(&src) { + format!("{src} as _") + } else { + format!("({src}) as _") } - snippet.push_str(" as _"); - } + } else { + src.to_owned() + }; err.multipart_suggestion( "return the expression directly", - vec![(local.span, String::new()), (retexpr.span, snippet)], + vec![(local.span, String::new()), (retexpr.span, sugg)], Applicability::MachineApplicable, ); } else { diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs index e05584296381c..44e585953bfb9 100644 --- a/clippy_lints/src/single_range_in_vec_init.rs +++ b/clippy_lints/src/single_range_in_vec_init.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_trait_def_id; use clippy_utils::higher::VecArgs; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; @@ -92,12 +92,12 @@ impl LateLintPass<'_> for SingleRangeInVecInit { if matches!(lang_item, LangItem::Range) && let ty = cx.typeck_results().expr_ty(start.expr) - && let Some(snippet) = snippet_opt(cx, span) + && let Some(snippet) = span.get_source_text(cx) // `is_from_proc_macro` will skip any `vec![]`. Let's not! && snippet.starts_with(suggested_type.starts_with()) && snippet.ends_with(suggested_type.ends_with()) - && let Some(start_snippet) = snippet_opt(cx, start.span) - && let Some(end_snippet) = snippet_opt(cx, end.span) + && let Some(start_snippet) = start.span.get_source_text(cx) + && let Some(end_snippet) = end.span.get_source_text(cx) { let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"]) && implements_trait(cx, ty, step_def_id, &[]) diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 59b74122f3084..2e87d36df3121 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -1,7 +1,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; -use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; +use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt}; use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash}; use core::hash::{Hash, Hasher}; use itertools::Itertools; @@ -206,8 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { let fixed_trait_snippet = unique_traits .iter() - .filter_map(|b| snippet_opt(cx, b.span)) - .collect::<Vec<_>>() + .filter_map(|b| b.span.get_source_text(cx)) .join(" + "); span_lint_and_sugg( @@ -462,9 +461,8 @@ fn rollup_traits( let traits = comparable_bounds .iter() - .filter_map(|&(_, span)| snippet_opt(cx, span)) - .collect::<Vec<_>>(); - let traits = traits.join(" + "); + .filter_map(|&(_, span)| span.get_source_text(cx)) + .join(" + "); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index b70aa768b46b1..65f431d338bdb 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{position_before_rarrow, snippet_opt}; +use clippy_utils::source::{position_before_rarrow, SpanRangeExt}; use rustc_ast::visit::FnKind; use rustc_ast::{ast, ClosureBinder}; use rustc_errors::Applicability; @@ -128,15 +128,17 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { let (ret_span, appl) = - snippet_opt(cx, span.with_hi(ty.span.hi())).map_or((ty.span, Applicability::MaybeIncorrect), |fn_source| { - position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { - ( - #[expect(clippy::cast_possible_truncation)] - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable, - ) - }) - }); + span.with_hi(ty.span.hi()) + .get_source_text(cx) + .map_or((ty.span, Applicability::MaybeIncorrect), |src| { + position_before_rarrow(&src).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + ( + #[expect(clippy::cast_possible_truncation)] + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + }) + }); span_lint_and_sugg( cx, UNUSED_UNIT, diff --git a/clippy_lints/src/visibility.rs b/clippy_lints/src/visibility.rs index 63f3a5d7f8305..7a85196cecaaa 100644 --- a/clippy_lints/src/visibility.rs +++ b/clippy_lints/src/visibility.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{Item, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; @@ -82,9 +82,7 @@ impl EarlyLintPass for Visibility { if !in_external_macro(cx.sess(), item.span) && let VisibilityKind::Restricted { path, shorthand, .. } = &item.vis.kind { - if **path == kw::SelfLower - && let Some(false) = is_from_proc_macro(cx, item.vis.span) - { + if **path == kw::SelfLower && !is_from_proc_macro(cx, item.vis.span) { span_lint_and_then( cx, NEEDLESS_PUB_SELF, @@ -104,7 +102,7 @@ impl EarlyLintPass for Visibility { if (**path == kw::Super || **path == kw::SelfLower || **path == kw::Crate) && !*shorthand && let [.., last] = &*path.segments - && let Some(false) = is_from_proc_macro(cx, item.vis.span) + && !is_from_proc_macro(cx, item.vis.span) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( @@ -125,7 +123,7 @@ impl EarlyLintPass for Visibility { if *shorthand && let [.., last] = &*path.segments - && let Some(false) = is_from_proc_macro(cx, item.vis.span) + && !is_from_proc_macro(cx, item.vis.span) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( @@ -147,6 +145,6 @@ impl EarlyLintPass for Visibility { } } -fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> Option<bool> { - snippet_opt(cx, span).map(|s| !s.starts_with("pub")) +fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> bool { + !span.check_source_text(cx, |src| src.starts_with("pub")) } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 54e7e92f0c45d..ec5a5896fb2bf 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::is_in_test; use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall}; -use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; +use clippy_utils::source::{expand_past_previous_comma, SpanRangeExt}; use rustc_ast::token::LitKind; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, @@ -397,7 +397,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma format!("using `{name}!()` with a format string that ends in a single newline"), |diag| { let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); - let Some(format_snippet) = snippet_opt(cx, format_string_span) else { + let Some(format_snippet) = format_string_span.get_source_text(cx) else { return; }; @@ -492,7 +492,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { && let Some(arg) = format_args.arguments.by_index(index) && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind && !arg.expr.span.from_expansion() - && let Some(value_string) = snippet_opt(cx, arg.expr.span) + && let Some(value_string) = arg.expr.span.get_source_text(cx) { let (replacement, replace_raw) = match lit.kind { LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) { @@ -515,7 +515,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { _ => continue, }; - let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { + let Some(format_string_snippet) = format_args.span.get_source_text(cx) else { continue; }; let format_string_is_raw = format_string_snippet.starts_with('r'); diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 1be1a9b28b9ea..8b49b013e0313 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -16,7 +16,7 @@ use rustc_span::{ }; use std::borrow::Cow; use std::fmt; -use std::ops::{Deref, Range}; +use std::ops::{Deref, Index, Range}; pub trait HasSession { fn sess(&self) -> &Session; @@ -186,6 +186,15 @@ impl AsRef<str> for SourceText { self.as_str() } } +impl<T> Index<T> for SourceText +where + str: Index<T>, +{ + type Output = <str as Index<T>>::Output; + fn index(&self, idx: T) -> &Self::Output { + &self.as_str()[idx] + } +} impl fmt::Display for SourceText { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.as_str().fmt(f) From d7f1252ddf7ae02667ac9de295171596c7075640 Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Mon, 12 Aug 2024 18:42:13 +0200 Subject: [PATCH 30/54] fix code blocks in doc comments inconsistently using 3 or 4 spaces of indentation --- clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/methods/mod.rs | 6 +++--- clippy_lints/src/utils/internal_lints/metadata_collector.rs | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index d7b3a7c74f3ce..762d5595e145e 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -309,7 +309,7 @@ declare_clippy_lint! { /// ### Known problems /// Inner doc comments can only appear before items, so there are certain cases where the suggestion /// made by this lint is not valid code. For example: - /// ```rs + /// ```rust /// fn foo() {} /// ///! /// fn bar() {} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1d7b10fe8f045..d7126990edb1d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2702,10 +2702,10 @@ declare_clippy_lint! { /// } /// }) /// } - /// ``` + /// ``` /// - /// After: - /// ```rust + /// After: + /// ```rust /// use std::{fmt, num::ParseIntError}; /// /// #[derive(Debug)] diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 57f45aa3e4851..c73ec262637f7 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -687,10 +687,12 @@ fn cleanup_docs(docs_collection: &Vec<String>) -> String { .trim() .split(',') // remove rustdoc directives - .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic")) + .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic" | "compile_fail")) // if no language is present, fill in "rust" .unwrap_or("rust"); - let len_diff = line.len() - line.trim_start().len(); + let len_diff = line + .strip_prefix(' ') + .map_or(0, |line| line.len() - line.trim_start().len()); if len_diff != 0 { // We put back the indentation. docs.push_str(&line[..len_diff]); From 182cd5f2785ae77f3a05cf75ee301ed8850c8d0a Mon Sep 17 00:00:00 2001 From: Alex Macleod <alex@macleod.io> Date: Mon, 5 Aug 2024 14:08:04 +0000 Subject: [PATCH 31/54] Replace the metadata collector with tests --- .cargo/config.toml | 6 +- .github/workflows/clippy_bors.yml | 5 - Cargo.toml | 4 +- book/src/development/adding_lints.md | 2 +- book/src/lint_configuration.md | 4 +- clippy_dev/src/update_lints.rs | 2 +- clippy_lints/src/declared_lints.rs | 4 +- clippy_lints/src/lib.rs | 48 +- clippy_lints/src/utils/internal_lints.rs | 1 - .../internal_lints/metadata_collector.rs | 1080 ----------------- declare_clippy_lint/src/lib.rs | 25 +- tests/compile-test.rs | 382 ++++-- tests/config-metadata.rs | 76 ++ util/gh-pages/index.html | 6 +- util/gh-pages/script.js | 9 +- 15 files changed, 448 insertions(+), 1206 deletions(-) delete mode 100644 clippy_lints/src/utils/internal_lints/metadata_collector.rs create mode 100644 tests/config-metadata.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index ce07290d1e1e5..d9c635df5dc79 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,10 +1,10 @@ [alias] +bless = "test --config env.RUSTC_BLESS='1'" uitest = "test --test compile-test" -uibless = "test --test compile-test -- -- --bless" -bless = "test -- -- --bless" +uibless = "bless --test compile-test" dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- " -collect-metadata = "test --test dogfood --features internal -- collect_metadata" +collect-metadata = "test --test compile-test --config env.COLLECT_METADATA='1'" [build] # -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 10e18e84c89fd..2aa13313fa518 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -136,11 +136,6 @@ jobs: - name: Test metadata collection run: cargo collect-metadata - - name: Test lint_configuration.md is up-to-date - run: | - echo "run \`cargo collect-metadata\` if this fails" - git update-index --refresh - integration_build: needs: changelog runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 78409c7a09e29..b48b881097f47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,8 +30,11 @@ color-print = "0.3.4" anstream = "0.6.0" [dev-dependencies] +cargo_metadata = "0.18.1" ui_test = "0.25" regex = "1.5.5" +serde = { version = "1.0.145", features = ["derive"] } +serde_json = "1.0.122" toml = "0.7.3" walkdir = "2.3" filetime = "0.2.9" @@ -41,7 +44,6 @@ itertools = "0.12" clippy_utils = { path = "clippy_utils" } if_chain = "1.0" quote = "1.0.25" -serde = { version = "1.0.145", features = ["derive"] } syn = { version = "2.0", features = ["full"] } futures = "0.3" parking_lot = "0.12" diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index a71d94daca74c..963e02e5c1616 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -739,7 +739,7 @@ for some users. Adding a configuration is done in the following steps: 5. Update [Lint Configuration](../lint_configuration.md) - Run `cargo collect-metadata` to generate documentation changes for the book. + Run `cargo bless --test config-metadata` to generate documentation changes for the book. [`clippy_config::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_config/src/conf.rs [`clippy_lints` lib file]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index e3d550b146629..4f23afe2e118a 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -1,5 +1,5 @@ <!-- -This file is generated by `cargo collect-metadata`. +This file is generated by `cargo bless --test config-metadata`. Please use that command to update the file and do not edit it by hand. --> @@ -949,5 +949,3 @@ Whether to also emit warnings for unsafe blocks with metavariable expansions in --- **Affected lints:** * [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe) - - diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 15578d69c3a9d..8dbda1c634c28 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -604,7 +604,7 @@ fn gen_declared_lints<'a>( details.sort_unstable(); let mut output = GENERATED_FILE_COMMENT.to_string(); - output.push_str("pub(crate) static LINTS: &[&crate::LintInfo] = &[\n"); + output.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n"); for (is_public, module_name, lint_name) in details { if !is_public { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 3fb083dd83310..8754a4dff87b1 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -2,7 +2,7 @@ // Use that command to update this file and do not edit by hand. // Manual edits will be overwritten. -pub(crate) static LINTS: &[&crate::LintInfo] = &[ +pub static LINTS: &[&crate::LintInfo] = &[ #[cfg(feature = "internal")] crate::utils::internal_lints::almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION_INFO, #[cfg(feature = "internal")] @@ -22,8 +22,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ #[cfg(feature = "internal")] crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO, #[cfg(feature = "internal")] - crate::utils::internal_lints::metadata_collector::METADATA_COLLECTOR_INFO, - #[cfg(feature = "internal")] crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ce13a9afef5b0..2ac06b360bea4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -66,8 +66,8 @@ extern crate declare_clippy_lint; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; -mod declared_lints; -mod deprecated_lints; +pub mod declared_lints; +pub mod deprecated_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absolute_paths; @@ -440,7 +440,7 @@ impl RegistrationGroups { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub(crate) enum LintCategory { Cargo, Complexity, @@ -479,11 +479,39 @@ impl LintCategory { } } -pub(crate) struct LintInfo { +pub struct LintInfo { /// Double reference to maintain pointer equality - lint: &'static &'static Lint, + pub lint: &'static &'static Lint, category: LintCategory, - explanation: &'static str, + pub explanation: &'static str, + /// e.g. `clippy_lints/src/absolute_paths.rs#43` + pub location: &'static str, + pub version: Option<&'static str>, +} + +impl LintInfo { + /// Returns the lint name in lowercase without the `clippy::` prefix + #[allow(clippy::missing_panics_doc)] + pub fn name_lower(&self) -> String { + self.lint.name.strip_prefix("clippy::").unwrap().to_ascii_lowercase() + } + + /// Returns the name of the lint's category in lowercase (`style`, `pedantic`) + pub fn category_str(&self) -> &'static str { + match self.category { + Cargo => "cargo", + Complexity => "complexity", + Correctness => "correctness", + Nursery => "nursery", + Pedantic => "pedantic", + Perf => "perf", + Restriction => "restriction", + Style => "style", + Suspicious => "suspicious", + #[cfg(feature = "internal")] + Internal => "internal", + } + } } pub fn explain(name: &str) -> i32 { @@ -538,14 +566,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_removed(name, reason); } - #[cfg(feature = "internal")] - { - if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { - store.register_late_pass(|_| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new())); - return; - } - } - let format_args_storage = FormatArgsStorage::default(); let format_args = format_args_storage.clone(); store.register_early_pass(move || { diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 1d294c2944b2c..f662c7651f6bb 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -3,7 +3,6 @@ pub mod collapsible_calls; pub mod interning_defined_symbol; pub mod invalid_paths; pub mod lint_without_lint_pass; -pub mod metadata_collector; pub mod msrv_attr_impl; pub mod outer_expn_data_pass; pub mod produce_ice; diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs deleted file mode 100644 index c73ec262637f7..0000000000000 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ /dev/null @@ -1,1080 +0,0 @@ -//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json -//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html) -//! -//! This module and therefore the entire lint is guarded by a feature flag called `internal` -//! -//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches -//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such -//! a simple mistake) - -use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}; -use clippy_config::{get_configuration_metadata, ClippyConfiguration}; - -use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; -use clippy_utils::{last_path_segment, match_function_call, match_path, paths}; -use itertools::Itertools; -use rustc_ast as ast; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::DefKind; -use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, intravisit, Closure, ExprKind, Item, ItemKind, Mutability, QPath}; -use rustc_lint::{unerased_lint_store, CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; -use rustc_middle::hir::nested_filter; -use rustc_session::impl_lint_pass; -use rustc_span::symbol::Ident; -use rustc_span::{sym, Loc, Span, Symbol}; -use serde::ser::SerializeStruct; -use serde::{Serialize, Serializer}; -use std::collections::{BTreeSet, BinaryHeap}; -use std::fmt::Write as _; -use std::fs::{self, File}; -use std::io::prelude::*; -use std::path::{Path, PathBuf}; -use std::process::Command; -use std::{env, fmt}; - -/// This is the json output file of the lint collector. -const JSON_OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; -/// This is the markdown output file of the lint collector. -const MARKDOWN_OUTPUT_FILE: &str = "../book/src/lint_configuration.md"; -/// These groups will be ignored by the lint group matcher. This is useful for collections like -/// `clippy::all` -const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"]; -/// Lints within this group will be excluded from the collection. These groups -/// have to be defined without the `clippy::` prefix. -const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"]; -/// Collected deprecated lint will be assigned to this group in the JSON output -const DEPRECATED_LINT_GROUP_STR: &str = "deprecated"; -/// This is the lint level for deprecated lints that will be displayed in the lint list -const DEPRECATED_LINT_LEVEL: &str = "none"; -/// This array holds Clippy's lint groups with their corresponding default lint level. The -/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`. -const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[ - ("correctness", "deny"), - ("suspicious", "warn"), - ("restriction", "allow"), - ("style", "warn"), - ("pedantic", "allow"), - ("complexity", "warn"), - ("perf", "warn"), - ("cargo", "allow"), - ("nursery", "allow"), -]; -/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed -/// to only keep the actual lint group in the output. -const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::"; -const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [ - &["clippy_utils", "diagnostics", "span_lint"], - &["clippy_utils", "diagnostics", "span_lint_and_help"], - &["clippy_utils", "diagnostics", "span_lint_and_note"], - &["clippy_utils", "diagnostics", "span_lint_hir"], - &["clippy_utils", "diagnostics", "span_lint_and_sugg"], - &["clippy_utils", "diagnostics", "span_lint_and_then"], - &["clippy_utils", "diagnostics", "span_lint_hir_and_then"], -]; -const SUGGESTION_DIAG_METHODS: [(&str, bool); 9] = [ - ("span_suggestion", false), - ("span_suggestion_short", false), - ("span_suggestion_verbose", false), - ("span_suggestion_hidden", false), - ("tool_only_span_suggestion", false), - ("multipart_suggestion", true), - ("multipart_suggestions", true), - ("tool_only_multipart_suggestion", true), - ("span_suggestions", true), -]; - -/// The index of the applicability name of `paths::APPLICABILITY_VALUES` -const APPLICABILITY_NAME_INDEX: usize = 2; -/// This applicability will be set for unresolved applicability values. -const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved"; -/// The version that will be displayed if none has been defined -const VERSION_DEFAULT_STR: &str = "Unknown"; - -const CHANGELOG_PATH: &str = "../CHANGELOG.md"; - -declare_clippy_lint! { - /// ### What it does - /// Collects metadata about clippy lints for the website. - /// - /// This lint will be used to report problems of syntax parsing. You should hopefully never - /// see this but never say never I guess ^^ - /// - /// ### Why is this bad? - /// This is not a bad thing but definitely a hacky way to do it. See - /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion - /// about the implementation. - /// - /// ### Known problems - /// Hopefully none. It would be pretty uncool to have a problem here :) - /// - /// ### Example output - /// ```json,ignore - /// { - /// "id": "metadata_collector", - /// "id_span": { - /// "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs", - /// "line": 1 - /// }, - /// "group": "clippy::internal", - /// "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] " - /// } - /// ``` - #[clippy::version = "1.56.0"] - pub METADATA_COLLECTOR, - internal, - "A busy bee collection metadata about lints" -} - -impl_lint_pass!(MetadataCollector => [METADATA_COLLECTOR]); - -#[allow(clippy::module_name_repetitions)] -#[derive(Debug, Clone)] -pub struct MetadataCollector { - /// All collected lints - /// - /// We use a Heap here to have the lints added in alphabetic order in the export - lints: BinaryHeap<LintMetadata>, - applicability_info: FxHashMap<String, ApplicabilityInfo>, - config: Vec<ClippyConfiguration>, - clippy_project_root: PathBuf, -} - -impl MetadataCollector { - pub fn new() -> Self { - Self { - lints: BinaryHeap::<LintMetadata>::default(), - applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(), - config: get_configuration_metadata(), - clippy_project_root: env::current_dir() - .expect("failed to get current dir") - .ancestors() - .nth(1) - .expect("failed to get project root") - .to_path_buf(), - } - } - - fn get_lint_configs(&self, lint_name: &str) -> Option<String> { - self.config - .iter() - .filter(|config| config.lints.iter().any(|&lint| lint == lint_name)) - .map(ToString::to_string) - .reduce(|acc, x| acc + "\n\n" + &x) - .map(|configurations| { - format!( - r#" -### Configuration -This lint has the following configuration variables: - -{configurations} -"# - ) - }) - } - - fn configs_to_markdown(&self, map_fn: fn(&ClippyConfiguration) -> String) -> String { - self.config - .iter() - .filter(|config| config.deprecation_reason.is_none()) - .filter(|config| !config.lints.is_empty()) - .map(map_fn) - .join("\n") - } - - fn get_markdown_docs(&self) -> String { - format!( - r#"# Lint Configuration Options - -The following list shows each configuration option, along with a description, its default value, an example -and lints affected. - ---- - -{}"#, - self.configs_to_markdown(ClippyConfiguration::to_markdown_paragraph), - ) - } -} - -impl Drop for MetadataCollector { - /// You might ask: How hacky is this? - /// My answer: YES - fn drop(&mut self) { - // The metadata collector gets dropped twice, this makes sure that we only write - // when the list is full - if self.lints.is_empty() { - return; - } - - let mut applicability_info = std::mem::take(&mut self.applicability_info); - - // Add deprecated lints - self.lints.extend( - crate::deprecated_lints::DEPRECATED - .iter() - .zip(crate::deprecated_lints::DEPRECATED_VERSION) - .filter_map(|((lint, reason), version)| LintMetadata::new_deprecated(lint, reason, version)), - ); - // Mapping the final data - let mut lints = std::mem::take(&mut self.lints).into_sorted_vec(); - for x in &mut lints { - x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default()); - replace_produces(&x.id, &mut x.docs, &self.clippy_project_root); - } - - collect_renames(&mut lints); - - // Outputting json - fs::write(JSON_OUTPUT_FILE, serde_json::to_string_pretty(&lints).unwrap()).unwrap(); - - // Outputting markdown - let mut file = File::create(MARKDOWN_OUTPUT_FILE).unwrap(); - writeln!( - file, - "<!-- -This file is generated by `cargo collect-metadata`. -Please use that command to update the file and do not edit it by hand. ---> - -{}", - self.get_markdown_docs(), - ) - .unwrap(); - - // Write configuration links to CHANGELOG.md - let changelog = fs::read_to_string(CHANGELOG_PATH).unwrap(); - let mut changelog_file = File::create(CHANGELOG_PATH).unwrap(); - let position = changelog - .find("<!-- begin autogenerated links to configuration documentation -->") - .unwrap(); - writeln!( - changelog_file, - "{}<!-- begin autogenerated links to configuration documentation -->\n{}\n<!-- end autogenerated links to configuration documentation -->", - &changelog[..position], - self.configs_to_markdown(ClippyConfiguration::to_markdown_link) - ) - .unwrap(); - } -} - -#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] -struct LintMetadata { - id: String, - id_span: Option<SerializableSpan>, - group: String, - level: String, - docs: String, - version: String, - /// This field is only used in the output and will only be - /// mapped shortly before the actual output. - applicability: Option<ApplicabilityInfo>, - /// All the past names of lints which have been renamed. - #[serde(skip_serializing_if = "BTreeSet::is_empty")] - former_ids: BTreeSet<String>, -} - -impl LintMetadata { - fn new( - id: String, - id_span: SerializableSpan, - group: String, - level: &'static str, - version: String, - docs: String, - ) -> Self { - Self { - id, - id_span: Some(id_span), - group, - level: level.to_string(), - version, - docs, - applicability: None, - former_ids: BTreeSet::new(), - } - } - - fn new_deprecated(name: &str, reason: &str, version: &str) -> Option<Self> { - // The reason starts with a lowercase letter and end without a period. - // This needs to be fixed for the website. - let mut reason = reason.to_owned(); - if let Some(reason) = reason.get_mut(0..1) { - reason.make_ascii_uppercase(); - } - name.strip_prefix("clippy::").map(|name| Self { - id: name.into(), - id_span: None, - group: DEPRECATED_LINT_GROUP_STR.into(), - level: DEPRECATED_LINT_LEVEL.into(), - version: version.into(), - docs: format!( - "### What it does\n\n\ - Nothing. This lint has been deprecated\n\n\ - ### Deprecation reason\n\n{reason}.\n", - ), - applicability: None, - former_ids: BTreeSet::new(), - }) - } -} - -fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) { - let mut doc_lines = docs.lines().map(ToString::to_string).collect::<Vec<_>>(); - let mut lines = doc_lines.iter_mut(); - - 'outer: loop { - // Find the start of the example - - // ```rust - loop { - match lines.next() { - Some(line) if line.trim_start().starts_with("```rust") => { - if line.contains("ignore") || line.contains("no_run") { - // A {{produces}} marker may have been put on a ignored code block by mistake, - // just seek to the end of the code block and continue checking. - if lines.any(|line| line.trim_start().starts_with("```")) { - continue; - } - - panic!("lint `{lint_name}` has an unterminated code block") - } - - break; - }, - Some(line) if line.trim_start() == "{{produces}}" => { - panic!("lint `{lint_name}` has marker {{{{produces}}}} with an ignored or missing code block") - }, - Some(line) => { - let line = line.trim(); - // These are the two most common markers of the corrections section - if line.eq_ignore_ascii_case("Use instead:") || line.eq_ignore_ascii_case("Could be written as:") { - break 'outer; - } - }, - None => break 'outer, - } - } - - // Collect the example - let mut example = Vec::new(); - loop { - match lines.next() { - Some(line) if line.trim_start() == "```" => break, - Some(line) => example.push(line), - None => panic!("lint `{lint_name}` has an unterminated code block"), - } - } - - // Find the {{produces}} and attempt to generate the output - loop { - match lines.next() { - Some(line) if line.is_empty() => {}, - Some(line) if line.trim() == "{{produces}}" => { - let output = get_lint_output(lint_name, &example, clippy_project_root); - line.replace_range( - .., - &format!( - "<details>\ - <summary>Produces</summary>\n\ - \n\ - ```text\n\ - {output}\n\ - ```\n\ - </details>" - ), - ); - - break; - }, - // No {{produces}}, we can move on to the next example - Some(_) => break, - None => break 'outer, - } - } - } - - *docs = cleanup_docs(&doc_lines); -} - -fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root: &Path) -> String { - let dir = tempfile::tempdir().unwrap_or_else(|e| panic!("failed to create temp dir: {e}")); - let file = dir.path().join("lint_example.rs"); - - let mut source = String::new(); - let unhidden = example - .iter() - .map(|line| line.trim_start().strip_prefix("# ").unwrap_or(line)); - - // Get any attributes - let mut lines = unhidden.peekable(); - while let Some(line) = lines.peek() { - if line.starts_with("#!") { - source.push_str(line); - source.push('\n'); - lines.next(); - } else { - break; - } - } - - let needs_main = !example.iter().any(|line| line.contains("fn main")); - if needs_main { - source.push_str("fn main() {\n"); - } - - for line in lines { - source.push_str(line); - source.push('\n'); - } - - if needs_main { - source.push_str("}\n"); - } - - if let Err(e) = fs::write(&file, &source) { - panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy()); - } - - let prefixed_name = format!("{CLIPPY_LINT_GROUP_PREFIX}{lint_name}"); - - let mut cmd = Command::new(env::var("CARGO").unwrap_or("cargo".into())); - - cmd.current_dir(clippy_project_root) - .env("CARGO_INCREMENTAL", "0") - .env("CLIPPY_ARGS", "") - .env("CLIPPY_DISABLE_DOCS_LINKS", "1") - // We need to disable this to enable all lints - .env("ENABLE_METADATA_COLLECTION", "0") - .args(["run", "--bin", "clippy-driver"]) - .args(["--target-dir", "./clippy_lints/target"]) - .args(["--", "--error-format=json"]) - .args(["--edition", "2021"]) - .arg("-Cdebuginfo=0") - .args(["-A", "clippy::all"]) - .args(["-W", &prefixed_name]) - .args(["-L", "./target/debug"]) - .args(["-Z", "no-codegen"]); - - let output = cmd - .arg(file.as_path()) - .output() - .unwrap_or_else(|e| panic!("failed to run `{cmd:?}`: {e}")); - - let tmp_file_path = file.to_string_lossy(); - let stderr = std::str::from_utf8(&output.stderr).unwrap(); - let msgs = stderr - .lines() - .filter(|line| line.starts_with('{')) - .map(|line| serde_json::from_str(line).unwrap()) - .collect::<Vec<serde_json::Value>>(); - - let mut rendered = String::new(); - let iter = msgs - .iter() - .filter(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s == &prefixed_name)); - - for message in iter { - let rendered_part = message["rendered"].as_str().expect("rendered field should exist"); - rendered.push_str(rendered_part); - } - - if rendered.is_empty() { - let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect(); - let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect(); - panic!( - "did not find lint `{lint_name}` in output of example, got:\n{}\n{}", - non_json.join("\n"), - rendered.join("\n") - ); - } - - // The reader doesn't need to see `/tmp/.tmpfiy2Qd/lint_example.rs` :) - rendered.trim_end().replace(&*tmp_file_path, "lint_example.rs") -} - -#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] -struct SerializableSpan { - path: String, - line: usize, -} - -impl fmt::Display for SerializableSpan { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line) - } -} - -impl SerializableSpan { - fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self { - Self::from_span(cx, item.ident.span) - } - - fn from_span(cx: &LateContext<'_>, span: Span) -> Self { - let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo()); - - Self { - path: format!("{}", loc.file.name.prefer_remapped_unconditionaly()), - line: loc.line, - } - } -} - -#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)] -struct ApplicabilityInfo { - /// Indicates if any of the lint emissions uses multiple spans. This is related to - /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can - /// currently not be applied automatically. - is_multi_part_suggestion: bool, - applicability: Option<usize>, -} - -impl Serialize for ApplicabilityInfo { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?; - s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?; - if let Some(index) = self.applicability { - s.serialize_field( - "applicability", - &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX], - )?; - } else { - s.serialize_field("applicability", APPLICABILITY_UNRESOLVED_STR)?; - } - s.end() - } -} - -// ================================================================== -// Lint pass -// ================================================================== -impl<'hir> LateLintPass<'hir> for MetadataCollector { - /// Collecting lint declarations like: - /// ```rust, ignore - /// declare_clippy_lint! { - /// /// ### What it does - /// /// Something IDK. - /// pub SOME_LINT, - /// internal, - /// "Who am I?" - /// } - /// ``` - fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) { - if let ItemKind::Static(ty, Mutability::Not, _) = item.kind { - // Normal lint - if is_lint_ref_type(cx, ty) - // item validation - // disallow check - && let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase() - // metadata extraction - && let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item) - && let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item) - { - if let Some(configuration_section) = self.get_lint_configs(&lint_name) { - raw_docs.push_str(&configuration_section); - } - let version = get_lint_version(cx, item); - - self.lints.push(LintMetadata::new( - lint_name, - SerializableSpan::from_item(cx, item), - group, - level, - version, - raw_docs, - )); - } - } - } - - /// Collecting constant applicability from the actual lint emissions - /// - /// Example: - /// ```rust, ignore - /// span_lint_and_sugg( - /// cx, - /// SOME_LINT, - /// item.span, - /// "Le lint message", - /// "Here comes help:", - /// "#![allow(clippy::all)]", - /// Applicability::MachineApplicable, // <-- Extracts this constant value - /// ); - /// ``` - fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) { - if let Some(args) = match_lint_emission(cx, expr) { - let emission_info = extract_emission_info(cx, args); - if emission_info.is_empty() { - // See: - // - src/misc.rs:734:9 - // - src/methods/mod.rs:3545:13 - // - src/methods/mod.rs:3496:13 - // We are basically unable to resolve the lint name itself. - return; - } - - for (lint_name, applicability, is_multi_part) in emission_info { - let app_info = self.applicability_info.entry(lint_name).or_default(); - app_info.applicability = applicability; - app_info.is_multi_part_suggestion = is_multi_part; - } - } - } -} - -// ================================================================== -// Lint definition extraction -// ================================================================== -fn sym_to_string(sym: Symbol) -> String { - sym.as_str().to_string() -} - -fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> { - extract_attr_docs(cx, item).or_else(|| { - lint_collection_error_item(cx, item, "could not collect the lint documentation"); - None - }) -} - -/// This function collects all documentation that has been added to an item using -/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks -/// -/// ```ignore -/// #[doc = r"Hello world!"] -/// #[doc = r"=^.^="] -/// struct SomeItem {} -/// ``` -/// -/// Would result in `Hello world!\n=^.^=\n` -fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str); - - if let Some(line) = lines.next() { - let raw_docs = lines.fold(String::from(line.as_str()) + "\n", |s, line| s + line.as_str() + "\n"); - return Some(raw_docs); - } - - None -} - -/// This function may modify the doc comment to ensure that the string can be displayed using a -/// markdown viewer in Clippy's lint list. The following modifications could be applied: -/// * Removal of leading space after a new line. (Important to display tables) -/// * Ensures that code blocks only contain language information -fn cleanup_docs(docs_collection: &Vec<String>) -> String { - let mut in_code_block = false; - let mut is_code_block_rust = false; - - let mut docs = String::new(); - for line in docs_collection { - // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :) - if is_code_block_rust && line.trim_start().starts_with("# ") { - continue; - } - - // The line should be represented in the lint list, even if it's just an empty line - docs.push('\n'); - if let Some(info) = line.trim_start().strip_prefix("```") { - in_code_block = !in_code_block; - is_code_block_rust = false; - if in_code_block { - let lang = info - .trim() - .split(',') - // remove rustdoc directives - .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic" | "compile_fail")) - // if no language is present, fill in "rust" - .unwrap_or("rust"); - let len_diff = line - .strip_prefix(' ') - .map_or(0, |line| line.len() - line.trim_start().len()); - if len_diff != 0 { - // We put back the indentation. - docs.push_str(&line[..len_diff]); - } - docs.push_str("```"); - docs.push_str(lang); - - is_code_block_rust = lang == "rust"; - continue; - } - } - // This removes the leading space that the macro translation introduces - if let Some(stripped_doc) = line.strip_prefix(' ') { - docs.push_str(stripped_doc); - } else if !line.is_empty() { - docs.push_str(line); - } - } - - docs -} - -fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String { - extract_clippy_version_value(cx, item).map_or_else( - || VERSION_DEFAULT_STR.to_string(), - |version| version.as_str().to_string(), - ) -} - -fn get_lint_group_and_level_or_lint( - cx: &LateContext<'_>, - lint_name: &str, - item: &Item<'_>, -) -> Option<(String, &'static str)> { - let result = unerased_lint_store(cx.tcx.sess).check_lint_name( - lint_name, - Some(sym::clippy), - &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(), - ); - if let CheckLintNameResult::Tool(lint_lst, None) = result { - if let Some(group) = get_lint_group(cx, lint_lst[0]) { - if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) { - return None; - } - - if let Some(level) = get_lint_level_from_group(&group) { - Some((group, level)) - } else { - lint_collection_error_item( - cx, - item, - &format!("Unable to determine lint level for found group `{group}`"), - ); - None - } - } else { - lint_collection_error_item(cx, item, "Unable to determine lint group"); - None - } - } else { - lint_collection_error_item(cx, item, "Unable to find lint in lint_store"); - None - } -} - -fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> { - for (group_name, lints, _) in unerased_lint_store(cx.tcx.sess).get_lint_groups() { - if IGNORED_LINT_GROUPS.contains(&group_name) { - continue; - } - - if lints.iter().any(|group_lint| *group_lint == lint_id) { - let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name); - return Some((*group).to_string()); - } - } - - None -} - -fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> { - DEFAULT_LINT_LEVELS - .iter() - .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level)) -} - -fn collect_renames(lints: &mut Vec<LintMetadata>) { - for lint in lints { - let mut collected = String::new(); - let mut names = vec![lint.id.clone()]; - - loop { - if let Some(lint_name) = names.pop() { - for (k, v) in crate::deprecated_lints::RENAMED { - if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX) - && name == lint_name - && let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX) - { - lint.former_ids.insert(past_name.to_owned()); - writeln!(collected, "* `{past_name}`").unwrap(); - names.push(past_name.to_string()); - } - } - - continue; - } - - break; - } - - if !collected.is_empty() { - write!( - &mut lint.docs, - r#" -### Past names - -{collected} -"# - ) - .unwrap(); - } - } -} - -// ================================================================== -// Lint emission -// ================================================================== -fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) { - span_lint( - cx, - METADATA_COLLECTOR, - item.ident.span, - format!("metadata collection error for `{}`: {message}", item.ident.name), - ); -} - -// ================================================================== -// Applicability -// ================================================================== -/// This function checks if a given expression is equal to a simple lint emission function call. -/// It will return the function arguments if the emission matched any function. -fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> { - LINT_EMISSION_FUNCTIONS - .iter() - .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) -} - -fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> { - a.map_or(b, |a| a.max(b.unwrap_or_default()).into()) -} - -fn extract_emission_info<'hir>( - cx: &LateContext<'hir>, - args: &'hir [hir::Expr<'hir>], -) -> Vec<(String, Option<usize>, bool)> { - let mut lints = Vec::new(); - let mut applicability = None; - let mut multi_part = false; - - for arg in args { - let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg)); - - if match_type(cx, arg_ty, &paths::LINT) { - // If we found the lint arg, extract the lint name - let mut resolved_lints = resolve_lints(cx, arg); - lints.append(&mut resolved_lints); - } else if match_type(cx, arg_ty, &paths::APPLICABILITY) { - applicability = resolve_applicability(cx, arg); - } else if arg_ty.is_closure() { - multi_part |= check_is_multi_part(cx, arg); - applicability = applicability.or_else(|| resolve_applicability(cx, arg)); - } - } - - lints - .into_iter() - .map(|lint_name| (lint_name, applicability, multi_part)) - .collect() -} - -/// Resolves the possible lints that this expression could reference -fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> { - let mut resolver = LintResolver::new(cx); - resolver.visit_expr(expr); - resolver.lints -} - -/// This function tries to resolve the linked applicability to the given expression. -fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> { - let mut resolver = ApplicabilityResolver::new(cx); - resolver.visit_expr(expr); - resolver.complete() -} - -fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool { - if let ExprKind::Closure(&Closure { body, .. }) = closure_expr.kind { - let mut scanner = IsMultiSpanScanner::new(cx); - intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body)); - return scanner.is_multi_part(); - } else if let Some(local) = get_parent_local(cx, closure_expr) { - if let Some(local_init) = local.init { - return check_is_multi_part(cx, local_init); - } - } - - false -} - -struct LintResolver<'a, 'hir> { - cx: &'a LateContext<'hir>, - lints: Vec<String>, -} - -impl<'a, 'hir> LintResolver<'a, 'hir> { - fn new(cx: &'a LateContext<'hir>) -> Self { - Self { - cx, - lints: Vec::<String>::default(), - } - } -} - -impl<'a, 'hir> Visitor<'hir> for LintResolver<'a, 'hir> { - type NestedFilter = nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } - - fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { - if let ExprKind::Path(qpath) = &expr.kind - && let QPath::Resolved(_, path) = qpath - && let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)) - && match_type(self.cx, expr_ty, &paths::LINT) - { - if let hir::def::Res::Def(DefKind::Static { .. }, _) = path.res { - let lint_name = last_path_segment(qpath).ident.name; - self.lints.push(sym_to_string(lint_name).to_ascii_lowercase()); - } else if let Some(local) = get_parent_local(self.cx, expr) { - if let Some(local_init) = local.init { - intravisit::walk_expr(self, local_init); - } - } - } - - intravisit::walk_expr(self, expr); - } -} - -/// This visitor finds the highest applicability value in the visited expressions -struct ApplicabilityResolver<'a, 'hir> { - cx: &'a LateContext<'hir>, - /// This is the index of highest `Applicability` for `paths::APPLICABILITY_VALUES` - applicability_index: Option<usize>, -} - -impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> { - fn new(cx: &'a LateContext<'hir>) -> Self { - Self { - cx, - applicability_index: None, - } - } - - fn add_new_index(&mut self, new_index: usize) { - self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index)); - } - - fn complete(self) -> Option<usize> { - self.applicability_index - } -} - -impl<'a, 'hir> Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { - type NestedFilter = nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } - - fn visit_path(&mut self, path: &hir::Path<'hir>, _id: hir::HirId) { - for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() { - if match_path(path, enum_value) { - self.add_new_index(index); - return; - } - } - } - - fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { - let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)); - - if match_type(self.cx, expr_ty, &paths::APPLICABILITY) - && let Some(local) = get_parent_local(self.cx, expr) - && let Some(local_init) = local.init - { - intravisit::walk_expr(self, local_init); - }; - - intravisit::walk_expr(self, expr); - } -} - -/// This returns the parent local node if the expression is a reference one -fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::LetStmt<'hir>> { - if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind { - if let hir::def::Res::Local(local_hir) = path.res { - return get_parent_local_hir_id(cx, local_hir); - } - } - - None -} - -fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::LetStmt<'hir>> { - match cx.tcx.parent_hir_node(hir_id) { - hir::Node::LetStmt(local) => Some(local), - hir::Node::Pat(pattern) => get_parent_local_hir_id(cx, pattern.hir_id), - _ => None, - } -} - -/// This visitor finds the highest applicability value in the visited expressions -struct IsMultiSpanScanner<'a, 'hir> { - cx: &'a LateContext<'hir>, - suggestion_count: usize, -} - -impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> { - fn new(cx: &'a LateContext<'hir>) -> Self { - Self { - cx, - suggestion_count: 0, - } - } - - /// Add a new single expression suggestion to the counter - fn add_single_span_suggestion(&mut self) { - self.suggestion_count += 1; - } - - /// Signals that a suggestion with possible multiple spans was found - fn add_multi_part_suggestion(&mut self) { - self.suggestion_count += 2; - } - - /// Checks if the suggestions include multiple spans - fn is_multi_part(&self) -> bool { - self.suggestion_count > 1 - } -} - -impl<'a, 'hir> Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> { - type NestedFilter = nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } - - fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { - // Early return if the lint is already multi span - if self.is_multi_part() { - return; - } - - if let ExprKind::MethodCall(path, recv, _, _arg_span) = &expr.kind { - let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv)); - if match_type(self.cx, self_ty, &paths::DIAG) { - let called_method = path.ident.name.as_str().to_string(); - for (method_name, is_multi_part) in &SUGGESTION_DIAG_METHODS { - if *method_name == called_method { - if *is_multi_part { - self.add_multi_part_suggestion(); - } else { - self.add_single_span_suggestion(); - } - break; - } - } - } - } - - intravisit::walk_expr(self, expr); - } -} diff --git a/declare_clippy_lint/src/lib.rs b/declare_clippy_lint/src/lib.rs index ca070f6c250bf..fd366b6b26277 100644 --- a/declare_clippy_lint/src/lib.rs +++ b/declare_clippy_lint/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(let_chains)] +#![feature(let_chains, proc_macro_span)] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] @@ -23,6 +23,7 @@ fn parse_attr<const LEN: usize>(path: [&'static str; LEN], attr: &Attribute) -> struct ClippyLint { attrs: Vec<Attribute>, + version: Option<LitStr>, explanation: String, name: Ident, category: Ident, @@ -41,8 +42,14 @@ impl Parse for ClippyLint { let value = lit.value(); let line = value.strip_prefix(' ').unwrap_or(&value); - if line.starts_with("```") { - explanation += "```\n"; + if let Some(lang) = line.strip_prefix("```") { + let tag = lang.split_once(',').map_or(lang, |(left, _)| left); + if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") { + explanation += "```rust\n"; + } else { + explanation += line; + explanation.push('\n'); + } in_code = !in_code; } else if !(in_code && line.starts_with("# ")) { explanation += line; @@ -68,6 +75,7 @@ impl Parse for ClippyLint { Ok(Self { attrs, + version, explanation, name, category, @@ -123,6 +131,7 @@ impl Parse for ClippyLint { pub fn declare_clippy_lint(input: TokenStream) -> TokenStream { let ClippyLint { attrs, + version, explanation, name, category, @@ -146,6 +155,14 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream { (&mut category[0..1]).make_ascii_uppercase(); let category_variant = format_ident!("{category}"); + let name_span = name.span().unwrap(); + let location = format!("{}#{}", name_span.source_file().path().display(), name_span.line()); + + let version = match version { + Some(version) => quote!(Some(#version)), + None => quote!(None), + }; + let output = quote! { rustc_session::declare_tool_lint! { #(#attrs)* @@ -159,6 +176,8 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream { lint: &#name, category: crate::LintCategory::#category_variant, explanation: #explanation, + location: #location, + version: #version, }; }; diff --git a/tests/compile-test.rs b/tests/compile-test.rs index c7080e5dcdc9e..9754254cdd0df 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,22 +1,31 @@ +#![feature(rustc_private, let_chains)] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] +use cargo_metadata::diagnostic::{Applicability, Diagnostic}; +use cargo_metadata::Message; +use clippy_config::ClippyConfiguration; +use clippy_lints::declared_lints::LINTS; +use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED}; +use clippy_lints::LintInfo; +use serde::{Deserialize, Serialize}; +use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::custom_flags::rustfix::RustfixMode; +use ui_test::custom_flags::Flag; use ui_test::spanned::Spanned; +use ui_test::test_result::TestRun; use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, OutputConflictHandling}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use std::env::{self, set_var, var_os}; use std::ffi::{OsStr, OsString}; -use std::fs; +use std::fmt::Write; use std::path::{Path, PathBuf}; -use std::sync::LazyLock; -use test_utils::IS_RUSTC_TEST_SUITE; +use std::sync::mpsc::{channel, Sender}; +use std::{fs, iter, thread}; // Test dependencies may need an `extern crate` here to ensure that they show up // in the depinfo file (otherwise cargo thinks they are unused) -extern crate clippy_lints; -extern crate clippy_utils; extern crate futures; extern crate if_chain; extern crate itertools; @@ -52,7 +61,7 @@ static TEST_DEPENDENCIES: &[&str] = &[ /// dependencies must be added to Cargo.toml at the project root. Test /// dependencies that are not *directly* used by this test module require an /// `extern crate` declaration. -static EXTERN_FLAGS: LazyLock<Vec<String>> = LazyLock::new(|| { +fn extern_flags() -> Vec<String> { let current_exe_depinfo = { let mut path = env::current_exe().unwrap(); path.set_extension("d"); @@ -100,70 +109,93 @@ static EXTERN_FLAGS: LazyLock<Vec<String>> = LazyLock::new(|| { .into_iter() .map(|(name, path)| format!("--extern={name}={path}")) .collect() -}); +} // whether to run internal tests or not const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); -fn base_config(test_dir: &str) -> (Config, Args) { - let mut args = Args::test().unwrap(); - args.bless |= var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); - - let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())); - let mut config = Config { - output_conflict_handling: OutputConflictHandling::Error, - filter_files: env::var("TESTNAME") - .map(|filters| filters.split(',').map(str::to_string).collect()) - .unwrap_or_default(), - target: None, - bless_command: Some("cargo uibless".into()), - out_dir: target_dir.join("ui_test"), - ..Config::rustc(Path::new("tests").join(test_dir)) - }; - config.comment_defaults.base().exit_status = None.into(); - config.comment_defaults.base().require_annotations = None.into(); - config - .comment_defaults - .base() - .set_custom("rustfix", RustfixMode::Everything); - config.comment_defaults.base().diagnostic_code_prefix = Some(Spanned::dummy("clippy::".into())).into(); - config.with_args(&args); - let current_exe_path = env::current_exe().unwrap(); - let deps_path = current_exe_path.parent().unwrap(); - let profile_path = deps_path.parent().unwrap(); - - config.program.args.extend( - [ - "--emit=metadata", - "-Aunused", - "-Ainternal_features", - "-Zui-testing", - "-Zdeduplicate-diagnostics=no", - "-Dwarnings", - &format!("-Ldependency={}", deps_path.display()), - ] - .map(OsString::from), - ); - - config.program.args.extend(EXTERN_FLAGS.iter().map(OsString::from)); - // Prevent rustc from creating `rustc-ice-*` files the console output is enough. - config.program.envs.push(("RUSTC_ICE".into(), Some("0".into()))); +struct TestContext { + args: Args, + extern_flags: Vec<String>, + diagnostic_collector: Option<DiagnosticCollector>, + collector_thread: Option<thread::JoinHandle<()>>, +} - if let Some(host_libs) = option_env!("HOST_LIBS") { - let dep = format!("-Ldependency={}", Path::new(host_libs).join("deps").display()); - config.program.args.push(dep.into()); +impl TestContext { + fn new() -> Self { + let mut args = Args::test().unwrap(); + args.bless |= var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); + let (diagnostic_collector, collector_thread) = var_os("COLLECT_METADATA") + .is_some() + .then(DiagnosticCollector::spawn) + .unzip(); + Self { + args, + extern_flags: extern_flags(), + diagnostic_collector, + collector_thread, + } } - config.program.program = profile_path.join(if cfg!(windows) { - "clippy-driver.exe" - } else { - "clippy-driver" - }); - (config, args) + fn base_config(&self, test_dir: &str) -> Config { + let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())); + let mut config = Config { + output_conflict_handling: OutputConflictHandling::Error, + filter_files: env::var("TESTNAME") + .map(|filters| filters.split(',').map(str::to_string).collect()) + .unwrap_or_default(), + target: None, + bless_command: Some("cargo uibless".into()), + out_dir: target_dir.join("ui_test"), + ..Config::rustc(Path::new("tests").join(test_dir)) + }; + let defaults = config.comment_defaults.base(); + defaults.exit_status = None.into(); + defaults.require_annotations = None.into(); + defaults.diagnostic_code_prefix = Some(Spanned::dummy("clippy::".into())).into(); + defaults.set_custom("rustfix", RustfixMode::Everything); + if let Some(collector) = self.diagnostic_collector.clone() { + defaults.set_custom("diagnostic-collector", collector); + } + config.with_args(&self.args); + let current_exe_path = env::current_exe().unwrap(); + let deps_path = current_exe_path.parent().unwrap(); + let profile_path = deps_path.parent().unwrap(); + + config.program.args.extend( + [ + "--emit=metadata", + "-Aunused", + "-Ainternal_features", + "-Zui-testing", + "-Zdeduplicate-diagnostics=no", + "-Dwarnings", + &format!("-Ldependency={}", deps_path.display()), + ] + .map(OsString::from), + ); + + config.program.args.extend(self.extern_flags.iter().map(OsString::from)); + // Prevent rustc from creating `rustc-ice-*` files the console output is enough. + config.program.envs.push(("RUSTC_ICE".into(), Some("0".into()))); + + if let Some(host_libs) = option_env!("HOST_LIBS") { + let dep = format!("-Ldependency={}", Path::new(host_libs).join("deps").display()); + config.program.args.push(dep.into()); + } + + config.program.program = profile_path.join(if cfg!(windows) { + "clippy-driver.exe" + } else { + "clippy-driver" + }); + + config + } } -fn run_ui() { - let (mut config, args) = base_config("ui"); +fn run_ui(cx: &TestContext) { + let mut config = cx.base_config("ui"); config .program .envs @@ -173,30 +205,29 @@ fn run_ui() { vec![config], ui_test::default_file_filter, ui_test::default_per_file_config, - status_emitter::Text::from(args.format), + status_emitter::Text::from(cx.args.format), ) .unwrap(); } -fn run_internal_tests() { - // only run internal tests with the internal-tests feature +fn run_internal_tests(cx: &TestContext) { if !RUN_INTERNAL_TESTS { return; } - let (mut config, args) = base_config("ui-internal"); + let mut config = cx.base_config("ui-internal"); config.bless_command = Some("cargo uitest --features internal -- -- --bless".into()); ui_test::run_tests_generic( vec![config], ui_test::default_file_filter, ui_test::default_per_file_config, - status_emitter::Text::from(args.format), + status_emitter::Text::from(cx.args.format), ) .unwrap(); } -fn run_ui_toml() { - let (mut config, args) = base_config("ui-toml"); +fn run_ui_toml(cx: &TestContext) { + let mut config = cx.base_config("ui-toml"); config .comment_defaults @@ -214,19 +245,19 @@ fn run_ui_toml() { .envs .push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into()))); }, - status_emitter::Text::from(args.format), + status_emitter::Text::from(cx.args.format), ) .unwrap(); } // Allow `Default::default` as `OptWithSpan` is not nameable #[allow(clippy::default_trait_access)] -fn run_ui_cargo() { +fn run_ui_cargo(cx: &TestContext) { if IS_RUSTC_TEST_SUITE { return; } - let (mut config, args) = base_config("ui-cargo"); + let mut config = cx.base_config("ui-cargo"); config.program.input_file_flag = CommandBuilder::cargo().input_file_flag; config.program.out_dir_flag = CommandBuilder::cargo().out_dir_flag; config.program.args = vec!["clippy".into(), "--color".into(), "never".into(), "--quiet".into()]; @@ -261,23 +292,25 @@ fn run_ui_cargo() { .then(|| ui_test::default_any_file_filter(path, config) && !ignored_32bit(path)) }, |_config, _file_contents| {}, - status_emitter::Text::from(args.format), + status_emitter::Text::from(cx.args.format), ) .unwrap(); } fn main() { set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); + + let cx = TestContext::new(); + // The SPEEDTEST_* env variables can be used to check Clippy's performance on your PR. It runs the // affected test 1000 times and gets the average. if let Ok(speedtest) = std::env::var("SPEEDTEST") { println!("----------- STARTING SPEEDTEST -----------"); let f = match speedtest.as_str() { - "ui" => run_ui as fn(), - "cargo" => run_ui_cargo as fn(), - "toml" => run_ui_toml as fn(), - "internal" => run_internal_tests as fn(), - "ui-cargo-toml-metadata" => ui_cargo_toml_metadata as fn(), + "ui" => run_ui, + "cargo" => run_ui_cargo, + "toml" => run_ui_toml, + "internal" => run_internal_tests, _ => panic!("unknown speedtest: {speedtest} || accepted speedtests are: [ui, cargo, toml, internal]"), }; @@ -294,7 +327,7 @@ fn main() { let mut sum = 0; for _ in 0..iterations { let start = std::time::Instant::now(); - f(); + f(&cx); sum += start.elapsed().as_millis(); } println!( @@ -303,11 +336,17 @@ fn main() { sum / u128::from(iterations) ); } else { - run_ui(); - run_ui_toml(); - run_ui_cargo(); - run_internal_tests(); + run_ui(&cx); + run_ui_toml(&cx); + run_ui_cargo(&cx); + run_internal_tests(&cx); + drop(cx.diagnostic_collector); + ui_cargo_toml_metadata(); + + if let Some(thread) = cx.collector_thread { + thread.join().unwrap(); + } } } @@ -346,3 +385,180 @@ fn ui_cargo_toml_metadata() { ); } } + +#[derive(Deserialize)] +#[serde(untagged)] +enum DiagnosticOrMessage { + Diagnostic(Diagnostic), + Message(Message), +} + +/// Collects applicabilities from the diagnostics produced for each UI test, producing the +/// `util/gh-pages/lints.json` file used by <https://rust-lang.github.io/rust-clippy/> +#[derive(Debug, Clone)] +struct DiagnosticCollector { + sender: Sender<Vec<u8>>, +} + +impl DiagnosticCollector { + #[allow(clippy::assertions_on_constants)] + fn spawn() -> (Self, thread::JoinHandle<()>) { + assert!(!IS_RUSTC_TEST_SUITE && !RUN_INTERNAL_TESTS); + + let (sender, receiver) = channel::<Vec<u8>>(); + + let handle = thread::spawn(|| { + let mut applicabilities = HashMap::new(); + + for stderr in receiver { + for line in stderr.split(|&byte| byte == b'\n') { + let diag = match serde_json::from_slice(line) { + Ok(DiagnosticOrMessage::Diagnostic(diag)) => diag, + Ok(DiagnosticOrMessage::Message(Message::CompilerMessage(message))) => message.message, + _ => continue, + }; + + if let Some(lint) = diag.code.as_ref().and_then(|code| code.code.strip_prefix("clippy::")) { + let applicability = applicabilities + .entry(lint.to_string()) + .or_insert(Applicability::Unspecified); + let diag_applicability = diag + .children + .iter() + .flat_map(|child| &child.spans) + .filter_map(|span| span.suggestion_applicability.clone()) + .max_by_key(applicability_ord); + if let Some(diag_applicability) = diag_applicability + && applicability_ord(&diag_applicability) > applicability_ord(applicability) + { + *applicability = diag_applicability; + } + } + } + } + + let configs = clippy_config::get_configuration_metadata(); + let mut metadata: Vec<LintMetadata> = LINTS + .iter() + .map(|lint| LintMetadata::new(lint, &applicabilities, &configs)) + .chain( + iter::zip(DEPRECATED, DEPRECATED_VERSION) + .map(|((lint, reason), version)| LintMetadata::new_deprecated(lint, reason, version)), + ) + .collect(); + metadata.sort_unstable_by(|a, b| a.id.cmp(&b.id)); + + let json = serde_json::to_string_pretty(&metadata).unwrap(); + fs::write("util/gh-pages/lints.json", json).unwrap(); + }); + + (Self { sender }, handle) + } +} + +fn applicability_ord(applicability: &Applicability) -> u8 { + match applicability { + Applicability::MachineApplicable => 4, + Applicability::HasPlaceholders => 3, + Applicability::MaybeIncorrect => 2, + Applicability::Unspecified => 1, + _ => unimplemented!(), + } +} + +impl Flag for DiagnosticCollector { + fn post_test_action( + &self, + _config: &ui_test::per_test_config::TestConfig<'_>, + _cmd: &mut std::process::Command, + output: &std::process::Output, + _build_manager: &ui_test::build_manager::BuildManager<'_>, + ) -> Result<Vec<TestRun>, ui_test::Errored> { + if !output.stderr.is_empty() { + self.sender.send(output.stderr.clone()).unwrap(); + } + Ok(Vec::new()) + } + + fn clone_inner(&self) -> Box<dyn Flag> { + Box::new(self.clone()) + } + + fn must_be_unique(&self) -> bool { + true + } +} + +#[derive(Debug, Serialize)] +struct LintMetadata { + id: String, + id_location: Option<&'static str>, + group: &'static str, + level: &'static str, + docs: String, + version: &'static str, + applicability: Applicability, +} + +impl LintMetadata { + fn new(lint: &LintInfo, applicabilities: &HashMap<String, Applicability>, configs: &[ClippyConfiguration]) -> Self { + let name = lint.name_lower(); + let applicability = applicabilities + .get(&name) + .cloned() + .unwrap_or(Applicability::Unspecified); + let past_names = RENAMED + .iter() + .filter(|(_, new_name)| new_name.strip_prefix("clippy::") == Some(&name)) + .map(|(old_name, _)| old_name.strip_prefix("clippy::").unwrap()) + .collect::<Vec<_>>(); + let mut docs = lint.explanation.to_string(); + if !past_names.is_empty() { + docs.push_str("\n### Past names\n\n"); + for past_name in past_names { + writeln!(&mut docs, " * {past_name}").unwrap(); + } + } + let configs: Vec<_> = configs + .iter() + .filter(|conf| conf.lints.contains(&name.as_str())) + .collect(); + if !configs.is_empty() { + docs.push_str("\n### Configuration\n\n"); + for config in configs { + writeln!(&mut docs, "{config}").unwrap(); + } + } + Self { + id: name, + id_location: Some(lint.location), + group: lint.category_str(), + level: lint.lint.default_level.as_str(), + docs, + version: lint.version.unwrap(), + applicability, + } + } + + fn new_deprecated(name: &str, reason: &str, version: &'static str) -> Self { + // The reason starts with a lowercase letter and ends without a period. + // This needs to be fixed for the website. + let mut reason = reason.to_owned(); + if let Some(reason) = reason.get_mut(0..1) { + reason.make_ascii_uppercase(); + } + Self { + id: name.strip_prefix("clippy::").unwrap().into(), + id_location: None, + group: "deprecated", + level: "none", + version, + docs: format!( + "### What it does\n\n\ + Nothing. This lint has been deprecated\n\n\ + ### Deprecation reason\n\n{reason}.\n", + ), + applicability: Applicability::Unspecified, + } + } +} diff --git a/tests/config-metadata.rs b/tests/config-metadata.rs new file mode 100644 index 0000000000000..c0b048270601a --- /dev/null +++ b/tests/config-metadata.rs @@ -0,0 +1,76 @@ +use clippy_config::{get_configuration_metadata, ClippyConfiguration}; +use itertools::Itertools; +use regex::Regex; +use std::borrow::Cow; +use std::{env, fs}; + +fn metadata() -> impl Iterator<Item = ClippyConfiguration> { + get_configuration_metadata() + .into_iter() + .filter(|config| config.deprecation_reason.is_none()) + .filter(|config| !config.lints.is_empty()) +} + +#[test] +fn book() { + let path = "book/src/lint_configuration.md"; + let current = fs::read_to_string(path).unwrap(); + + let configs = metadata().map(|conf| conf.to_markdown_paragraph()).join("\n"); + let expected = format!( + r#"<!-- +This file is generated by `cargo bless --test config-metadata`. +Please use that command to update the file and do not edit it by hand. +--> + +# Lint Configuration Options + +The following list shows each configuration option, along with a description, its default value, an example +and lints affected. + +--- + +{} +"#, + configs.trim(), + ); + + if current != expected { + if env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0") { + fs::write(path, expected).unwrap(); + } else { + panic!("`{path}` is out of date, run `cargo bless --test config-metadata` to update it"); + } + } +} + +#[test] +fn changelog() { + let path = "CHANGELOG.md"; + let current = fs::read_to_string(path).unwrap(); + + let configs = metadata().map(|conf| conf.to_markdown_link()).join("\n"); + + let re = Regex::new( + "(?s)\ + (<!-- begin autogenerated links to configuration documentation -->)\ + .*\ + (<!-- end autogenerated links to configuration documentation -->)\ + ", + ) + .unwrap(); + let expected = re.replace(¤t, format!("$1\n{configs}\n$2")); + + assert!( + matches!(expected, Cow::Owned(_)), + "failed to find configuration section in `{path}`" + ); + + if current != expected { + if env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0") { + fs::write(path, expected.as_bytes()).unwrap(); + } else { + panic!("`{path}` is out of date, run `cargo bless --test config-metadata` to update it"); + } + } +} diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index dd58fa2282c64..f3d7e504fdf80 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -239,7 +239,7 @@ <h2 class="panel-title"> <!-- Applicability --> <div class="lint-additional-info-item"> <span> Applicability: </span> - <span class="label label-default label-applicability">{{lint.applicability.applicability}}</span> + <span class="label label-default label-applicability">{{lint.applicability}}</span> <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a> </div> <!-- Clippy version --> @@ -252,8 +252,8 @@ <h2 class="panel-title"> <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a> </div> <!-- Jump to source --> - <div class="lint-additional-info-item" ng-if="lint.id_span"> - <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/clippy_lints/{{lint.id_span.path}}#L{{lint.id_span.line}}">View Source</a> + <div class="lint-additional-info-item" ng-if="lint.id_location"> + <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/{{lint.id_location}}">View Source</a> </div> </div> </div> diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index d54b5e851c7c0..1a5330bc0e571 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -96,13 +96,13 @@ cargo: true, complexity: true, correctness: true, - deprecated: false, nursery: true, pedantic: true, perf: true, restriction: true, style: true, suspicious: true, + deprecated: false, } $scope.groups = { @@ -126,11 +126,10 @@ ); const APPLICABILITIES_FILTER_DEFAULT = { - Unspecified: true, - Unresolved: true, MachineApplicable: true, MaybeIncorrect: true, - HasPlaceholders: true + HasPlaceholders: true, + Unspecified: true, }; $scope.applicabilities = { @@ -425,7 +424,7 @@ } $scope.byApplicabilities = function (lint) { - return $scope.applicabilities[lint.applicability.applicability]; + return $scope.applicabilities[lint.applicability]; }; // Show details for one lint From a22ce2d00578bfdfbb079d8bacf626c2789d4b5a Mon Sep 17 00:00:00 2001 From: Alex Macleod <alex@macleod.io> Date: Mon, 12 Aug 2024 16:25:14 +0000 Subject: [PATCH 32/54] Remove `HashSet`s from `Conf` --- book/src/lint_configuration.md | 4 ++-- clippy_config/src/conf.rs | 23 +++++++++---------- clippy_config/src/lib.rs | 1 - clippy_lints/src/cargo/mod.rs | 6 ++--- clippy_lints/src/doc/mod.rs | 6 ++--- clippy_lints/src/min_ident_chars.rs | 4 ++-- .../src/operators/arithmetic_side_effects.rs | 2 +- clippy_lints/src/wildcard_imports.rs | 6 ++--- 8 files changed, 25 insertions(+), 27 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 4f23afe2e118a..78348797588a6 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -199,7 +199,7 @@ Allowed names below the minimum allowed characters. The value `".."` can be used the list to indicate, that the configured values should be appended to the default configuration of Clippy. By default, any configuration will replace the default value. -**Default Value:** `["j", "z", "i", "y", "n", "x", "w"]` +**Default Value:** `["i", "j", "x", "y", "z", "w", "n"]` --- **Affected lints:** @@ -455,7 +455,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["TiB", "CoreGraphics", "CoffeeScript", "TeX", "Direct2D", "PiB", "DirectX", "NetBSD", "OAuth", "NaN", "OpenType", "WebGL2", "WebTransport", "JavaScript", "OpenSSL", "OpenSSH", "EiB", "PureScript", "OpenAL", "MiB", "WebAssembly", "MinGW", "CoreFoundation", "WebGPU", "ClojureScript", "CamelCase", "OpenDNS", "NaNs", "OpenMP", "GitLab", "KiB", "sRGB", "CoreText", "macOS", "TypeScript", "GiB", "OpenExr", "YCbCr", "OpenTelemetry", "OpenBSD", "FreeBSD", "GPLv2", "PostScript", "WebP", "LaTeX", "TensorFlow", "AccessKit", "TrueType", "OpenStreetMap", "OpenGL", "DevOps", "OCaml", "WebRTC", "WebGL", "BibLaTeX", "GitHub", "GraphQL", "iOS", "Direct3D", "BibTeX", "DirectWrite", "GPLv3", "IPv6", "WebSocket", "IPv4", "ECMAScript"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "AccessKit", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 4c2a8255d6b65..a6f1b958bfb17 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -1,7 +1,6 @@ use crate::msrvs::Msrv; use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename}; use crate::ClippyConfiguration; -use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; @@ -218,7 +217,7 @@ macro_rules! define_Conf { define_Conf! { /// Which crates to allow absolute paths from #[lints(absolute_paths)] - absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default(), + absolute_paths_allowed_crates: Vec<String> = Vec::new(), /// The maximum number of segments a path can have before being linted, anything above this will /// be linted. #[lints(absolute_paths)] @@ -280,12 +279,12 @@ define_Conf! { allowed_dotfiles: Vec<String> = Vec::default(), /// A list of crate names to allow duplicates of #[lints(multiple_crate_versions)] - allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default(), + allowed_duplicate_crates: Vec<String> = Vec::new(), /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of /// the list to indicate, that the configured values should be appended to the default /// configuration of Clippy. By default, any configuration will replace the default value. #[lints(min_ident_chars)] - allowed_idents_below_min_chars: FxHashSet<String> = + allowed_idents_below_min_chars: Vec<String> = DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect(), /// List of prefixes to allow when determining whether an item's name ends with the module's name. /// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`), @@ -323,7 +322,7 @@ define_Conf! { /// 2. Paths with any segment that containing the word 'prelude' /// are already allowed by default. #[lints(wildcard_imports)] - allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default(), + allowed_wildcard_imports: Vec<String> = Vec::new(), /// Suppress checking of the passed type names in all types of operations. /// /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. @@ -355,7 +354,7 @@ define_Conf! { /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] /// ``` #[lints(arithmetic_side_effects)] - arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default(), + arithmetic_side_effects_allowed_binary: Vec<(String, String)> = <_>::default(), /// Suppress checking of the passed type names in unary operations like "negation" (`-`). /// /// #### Example @@ -431,7 +430,7 @@ define_Conf! { /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. #[lints(doc_markdown)] - doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(), + doc_valid_idents: Vec<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(), /// Whether to apply the raw pointer heuristic to determine if a type is `Send`. #[lints(non_send_fields_in_send_ty)] enable_raw_pointer_heuristic_for_send: bool = true, @@ -706,12 +705,12 @@ fn deserialize(file: &SourceFile) -> TryConf { DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS, ); // TODO: THIS SHOULD BE TESTED, this comment will be gone soon - if conf.conf.allowed_idents_below_min_chars.contains("..") { + if conf.conf.allowed_idents_below_min_chars.iter().any(|e| e == "..") { conf.conf .allowed_idents_below_min_chars .extend(DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string)); } - if conf.conf.doc_valid_idents.contains("..") { + if conf.conf.doc_valid_idents.iter().any(|e| e == "..") { conf.conf .doc_valid_idents .extend(DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string)); @@ -890,14 +889,14 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) { #[cfg(test)] mod tests { - use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use serde::de::IgnoredAny; + use std::collections::{HashMap, HashSet}; use std::fs; use walkdir::WalkDir; #[test] fn configs_are_tested() { - let mut names: FxHashSet<String> = crate::get_configuration_metadata() + let mut names: HashSet<String> = crate::get_configuration_metadata() .into_iter() .map(|meta| meta.name.replace('_', "-")) .collect(); @@ -910,7 +909,7 @@ mod tests { for entry in toml_files { let file = fs::read_to_string(entry.path()).unwrap(); #[allow(clippy::zero_sized_map_values)] - if let Ok(map) = toml::from_str::<FxHashMap<String, IgnoredAny>>(&file) { + if let Ok(map) = toml::from_str::<HashMap<String, IgnoredAny>>(&file) { for name in map.keys() { names.remove(name.as_str()); } diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index 3afe599c7b6e4..ac838cc81d2f0 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -15,7 +15,6 @@ extern crate rustc_ast; extern crate rustc_attr; -extern crate rustc_data_structures; #[allow(unused_extern_crates)] extern crate rustc_driver; extern crate rustc_errors; diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index 312ad4c299007..96a2b1614646e 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -205,7 +205,7 @@ declare_clippy_lint! { } pub struct Cargo { - allowed_duplicate_crates: &'static FxHashSet<String>, + allowed_duplicate_crates: FxHashSet<String>, ignore_publish: bool, } @@ -221,7 +221,7 @@ impl_lint_pass!(Cargo => [ impl Cargo { pub fn new(conf: &'static Conf) -> Self { Self { - allowed_duplicate_crates: &conf.allowed_duplicate_crates, + allowed_duplicate_crates: conf.allowed_duplicate_crates.iter().cloned().collect(), ignore_publish: conf.cargo_ignore_publish, } } @@ -263,7 +263,7 @@ impl LateLintPass<'_> for Cargo { { match MetadataCommand::new().exec() { Ok(metadata) => { - multiple_crate_versions::check(cx, &metadata, self.allowed_duplicate_crates); + multiple_crate_versions::check(cx, &metadata, &self.allowed_duplicate_crates); }, Err(e) => { for lint in WITH_DEPS_LINTS { diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 762d5595e145e..daea0d18e4060 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -423,14 +423,14 @@ declare_clippy_lint! { } pub struct Documentation { - valid_idents: &'static FxHashSet<String>, + valid_idents: FxHashSet<String>, check_private_items: bool, } impl Documentation { pub fn new(conf: &'static Conf) -> Self { Self { - valid_idents: &conf.doc_valid_idents, + valid_idents: conf.doc_valid_idents.iter().cloned().collect(), check_private_items: conf.check_private_items, } } @@ -452,7 +452,7 @@ impl_lint_pass!(Documentation => [ impl<'tcx> LateLintPass<'tcx> for Documentation { fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { - let Some(headers) = check_attrs(cx, self.valid_idents, attrs) else { + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return; }; diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index 53fa444e93c40..c83e5198c2748 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -41,14 +41,14 @@ declare_clippy_lint! { impl_lint_pass!(MinIdentChars => [MIN_IDENT_CHARS]); pub struct MinIdentChars { - allowed_idents_below_min_chars: &'static FxHashSet<String>, + allowed_idents_below_min_chars: FxHashSet<String>, min_ident_chars_threshold: u64, } impl MinIdentChars { pub fn new(conf: &'static Conf) -> Self { Self { - allowed_idents_below_min_chars: &conf.allowed_idents_below_min_chars, + allowed_idents_below_min_chars: conf.allowed_idents_below_min_chars.iter().cloned().collect(), min_ident_chars_threshold: conf.min_ident_chars_threshold, } } diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index bc71a4790b9d4..8b9f899d82d3f 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -35,7 +35,7 @@ impl ArithmeticSideEffects { ("f64", FxHashSet::from_iter(["f64"])), ("std::string::String", FxHashSet::from_iter(["str"])), ]); - for [lhs, rhs] in &conf.arithmetic_side_effects_allowed_binary { + for (lhs, rhs) in &conf.arithmetic_side_effects_allowed_binary { allowed_binary.entry(lhs).or_default().insert(rhs); } for s in &conf.arithmetic_side_effects_allowed { diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index c4d64ee4609ee..7d9e58ad2f8d5 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -100,14 +100,14 @@ declare_clippy_lint! { pub struct WildcardImports { warn_on_all: bool, - allowed_segments: &'static FxHashSet<String>, + allowed_segments: FxHashSet<String>, } impl WildcardImports { pub fn new(conf: &'static Conf) -> Self { Self { warn_on_all: conf.warn_on_all_wildcard_imports, - allowed_segments: &conf.allowed_wildcard_imports, + allowed_segments: conf.allowed_wildcard_imports.iter().cloned().collect(), } } } @@ -181,7 +181,7 @@ impl WildcardImports { fn check_exceptions(&self, cx: &LateContext<'_>, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool { item.span.from_expansion() || is_prelude_import(segments) - || is_allowed_via_config(segments, self.allowed_segments) + || is_allowed_via_config(segments, &self.allowed_segments) || (is_super_only_import(segments) && is_in_test(cx.tcx, item.hir_id())) } } From 5fee8408758ea1dfc33712065386d8a4c5213cd5 Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Tue, 13 Aug 2024 13:13:02 +0200 Subject: [PATCH 33/54] fix indentation in docs --- clippy_lints/src/attrs/mod.rs | 4 ++-- clippy_lints/src/operators/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 8f430ae601a85..5d99001e5aa3f 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -310,8 +310,8 @@ declare_clippy_lint! { /// ```rust,ignore /// #[allow(unused_mut)] /// fn foo() -> usize { - /// let mut a = Vec::new(); - /// a.len() + /// let mut a = Vec::new(); + /// a.len() /// } /// ``` /// Use instead: diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 9baecff801f21..9e8a821c3f4e8 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -80,7 +80,7 @@ declare_clippy_lint! { /// ```no_run /// // `n` can be any number, including `i32::MAX`. /// fn foo(n: i32) -> i32 { - /// n + 1 + /// n + 1 /// } /// ``` /// From 819fa6f1806c4432c98137e97e3b251ccf047ae8 Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Tue, 13 Aug 2024 13:13:31 +0200 Subject: [PATCH 34/54] fix source location github link --- declare_clippy_lint/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/declare_clippy_lint/src/lib.rs b/declare_clippy_lint/src/lib.rs index fd366b6b26277..6aa24329b0659 100644 --- a/declare_clippy_lint/src/lib.rs +++ b/declare_clippy_lint/src/lib.rs @@ -156,7 +156,7 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream { let category_variant = format_ident!("{category}"); let name_span = name.span().unwrap(); - let location = format!("{}#{}", name_span.source_file().path().display(), name_span.line()); + let location = format!("{}#L{}", name_span.source_file().path().display(), name_span.line()); let version = match version { Some(version) => quote!(Some(#version)), From f72b3dbba217eff3b7292517944948311552eaab Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote <n.nethercote@gmail.com> Date: Fri, 9 Aug 2024 17:44:47 +1000 Subject: [PATCH 35/54] Use `impl PartialEq<TokenKind> for Token` more. This lets us compare a `Token` with a `TokenKind`. It's used a lot, but can be used even more, avoiding the need for some `.kind` uses. --- clippy_dev/src/new_lint.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index de91233d196c8..87117832fb952 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -470,7 +470,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> }); // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl - while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) { + while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token == TokenKind::Ident) { let mut iter = iter .by_ref() .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); @@ -480,7 +480,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> // matches `!{` match_tokens!(iter, Bang OpenBrace); if let Some(LintDeclSearchResult { range, .. }) = - iter.find(|result| result.token_kind == TokenKind::CloseBrace) + iter.find(|result| result.token == TokenKind::CloseBrace) { last_decl_curly_offset = Some(range.end); } From f3e00339a7009e735dcc280eba62e8f5f30c4c98 Mon Sep 17 00:00:00 2001 From: Philipp Krones <hello@philkrones.com> Date: Thu, 15 Aug 2024 21:09:17 +0200 Subject: [PATCH 36/54] flip1995: 2 week vacation --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index dcf00e4e384b0..99b3560a06427 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -20,6 +20,7 @@ new_pr = true [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ + "flip1995", "matthiaskrgr", "giraffate", ] From 953c7ed41ef6e98a484c1d15f9b06169f49c56f4 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk <alexsemenyuk88@gmail.com> Date: Fri, 16 Aug 2024 16:17:24 +0500 Subject: [PATCH 37/54] Disable assigning_clones for tests --- clippy_lints/src/assigning_clones.rs | 3 ++- tests/ui/assigning_clones.fixed | 20 ++++++++++++++++++++ tests/ui/assigning_clones.rs | 20 ++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 6e336efbb90a2..55645d04eef36 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -3,7 +3,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_diag_trait_item, last_path_segment, local_is_initialized, path_to_local}; +use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -118,6 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { } ) && !clone_source_borrows_from_dest(cx, lhs, rhs.span) + && !is_in_test(cx.tcx, e.hir_id) { span_lint_and_then( cx, diff --git a/tests/ui/assigning_clones.fixed b/tests/ui/assigning_clones.fixed index b376d55a40250..09732d1a50ce0 100644 --- a/tests/ui/assigning_clones.fixed +++ b/tests/ui/assigning_clones.fixed @@ -396,3 +396,23 @@ impl<T: Clone> Clone for DerefWrapperWithClone<T> { *self = Self(source.0.clone()); } } + +#[cfg(test)] +mod test { + #[derive(Default)] + struct Data { + field: String, + } + + fn test_data() -> Data { + Data { + field: "default_value".to_string(), + } + } + + #[test] + fn test() { + let mut data = test_data(); + data.field = "override_value".to_owned(); + } +} diff --git a/tests/ui/assigning_clones.rs b/tests/ui/assigning_clones.rs index 11a5d4459c350..6be25ae17a55d 100644 --- a/tests/ui/assigning_clones.rs +++ b/tests/ui/assigning_clones.rs @@ -396,3 +396,23 @@ impl<T: Clone> Clone for DerefWrapperWithClone<T> { *self = Self(source.0.clone()); } } + +#[cfg(test)] +mod test { + #[derive(Default)] + struct Data { + field: String, + } + + fn test_data() -> Data { + Data { + field: "default_value".to_string(), + } + } + + #[test] + fn test() { + let mut data = test_data(); + data.field = "override_value".to_owned(); + } +} From c0373616d098fecd38b14b5e42f7d202964b363d Mon Sep 17 00:00:00 2001 From: Alex Macleod <alex@macleod.io> Date: Fri, 16 Aug 2024 15:28:42 +0000 Subject: [PATCH 38/54] Fix code snippet in from_str_radix_10 docs --- clippy_lints/src/from_str_radix_10.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 6ab7bbc2dfc0c..e5fa67d696427 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -22,8 +22,8 @@ declare_clippy_lint! { /// /// ### Known problems /// - /// This lint may suggest using (&<expression>).parse() instead of <expression>.parse() directly - /// in some cases, which is correct but adds unnecessary complexity to the code. + /// This lint may suggest using `(&<expression>).parse()` instead of `<expression>.parse()` + /// directly in some cases, which is correct but adds unnecessary complexity to the code. /// /// ### Example /// ```ignore From 4f661302c6c5fee303248ea784eb66caa0b0e1cd Mon Sep 17 00:00:00 2001 From: Alex Macleod <alex@macleod.io> Date: Sat, 17 Aug 2024 18:09:28 +0000 Subject: [PATCH 39/54] Replace `span_suggestion_with_style` with `_verbose` --- .../src/casts/cast_possible_truncation.rs | 6 ++---- clippy_lints/src/casts/fn_to_numeric_cast_any.rs | 5 ++--- clippy_lints/src/create_dir.rs | 5 ++--- clippy_lints/src/doc/lazy_continuation.rs | 11 ++++------- clippy_lints/src/doc/markdown.rs | 15 +++------------ clippy_lints/src/float_literal.rs | 8 +++----- .../src/functions/impl_trait_in_params.rs | 11 +++++------ clippy_lints/src/implicit_return.rs | 13 +++---------- clippy_lints/src/legacy_numeric_constants.rs | 5 ++--- clippy_lints/src/methods/get_unwrap.rs | 3 +-- 10 files changed, 27 insertions(+), 55 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 102fe25fc67b0..5708aae3f3eca 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -4,7 +4,7 @@ use clippy_utils::expr_or_init; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; -use rustc_errors::{Applicability, Diag, SuggestionStyle}; +use rustc_errors::{Applicability, Diag}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -190,12 +190,10 @@ fn offer_suggestion( format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, "..")) }; - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( expr.span, "... or use `try_from` and handle the error accordingly", suggestion, Applicability::Unspecified, - // always show the suggestion in a separate line - SuggestionStyle::ShowAlways, ); } diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs index 75de53f73ee7a..cb7267c2d1ce6 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -24,12 +24,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, expr.span, format!("casting function pointer `{from_snippet}` to `{cast_to}`"), |diag| { - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( expr.span, "did you mean to invoke the function?", format!("{from_snippet}() as {cast_to}"), applicability, - SuggestionStyle::ShowAlways, ); }, ); diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index b49a977dbeaf1..24570d8f4401c 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -46,7 +46,7 @@ impl LateLintPass<'_> for CreateDir { "calling `std::fs::create_dir` where there may be a better way", |diag| { let mut app = Applicability::MaybeIncorrect; - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( expr.span, "consider calling `std::fs::create_dir_all` instead", format!( @@ -54,7 +54,6 @@ impl LateLintPass<'_> for CreateDir { snippet_with_applicability(cx, arg.span, "..", &mut app) ), app, - SuggestionStyle::ShowAlways, ); }, ); diff --git a/clippy_lints/src/doc/lazy_continuation.rs b/clippy_lints/src/doc/lazy_continuation.rs index bd1cc46e1850e..771bcac244187 100644 --- a/clippy_lints/src/doc/lazy_continuation.rs +++ b/clippy_lints/src/doc/lazy_continuation.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use itertools::Itertools; -use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_span::{BytePos, Span}; use std::ops::Range; @@ -59,12 +59,11 @@ pub(super) fn check( && (doc_comment == "///" || doc_comment == "//!") { // suggest filling in a blank line - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( line_break_span.shrink_to_lo(), "if this should be its own paragraph, add a blank doc comment line", format!("\n{doc_comment}"), Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, ); if ccount > 0 || blockquote_level > 0 { diag.help("if this not intended to be a quote at all, escape it with `\\>`"); @@ -79,12 +78,11 @@ pub(super) fn check( if ccount == 0 && blockquote_level == 0 { // simpler suggestion style for indentation let indent = list_indentation - lcount; - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( span.shrink_to_hi(), "indent this line", std::iter::repeat(" ").take(indent).join(""), Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, ); diag.help("if this is supposed to be its own paragraph, add a blank line"); return; @@ -107,12 +105,11 @@ pub(super) fn check( suggested.push_str(text); } } - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( span, "add markers to start of line", suggested, Applicability::MachineApplicable, - SuggestionStyle::ShowAlways, ); diag.help("if this not intended to be a quote at all, escape it with `\\>`"); }); diff --git a/clippy_lints/src/doc/markdown.rs b/clippy_lints/src/doc/markdown.rs index 237badb3f233c..8cdaba88e5095 100644 --- a/clippy_lints/src/doc/markdown.rs +++ b/clippy_lints/src/doc/markdown.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_span::{BytePos, Pos, Span}; use url::Url; @@ -137,24 +137,15 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b } if has_underscore(word) || word.contains("::") || is_camel_case(word) || word.ends_with("()") { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_then( cx, DOC_MARKDOWN, span, "item in documentation is missing backticks", |diag| { + let mut applicability = Applicability::MachineApplicable; let snippet = snippet_with_applicability(cx, span, "..", &mut applicability); - diag.span_suggestion_with_style( - span, - "try", - format!("`{snippet}`"), - applicability, - // always show the suggestion in a separate line, since the - // inline presentation adds another pair of backticks - SuggestionStyle::ShowAlways, - ); + diag.span_suggestion_verbose(span, "try", format!("`{snippet}`"), applicability); }, ); } diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index f095c1add91f8..012ad8e1a2298 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal; use rustc_ast::ast::{self, LitFloatType, LitKind}; -use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, FloatTy}; @@ -117,12 +117,11 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { if type_suffix.is_none() { float_str.push_str(".0"); } - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( expr.span, "consider changing the type or replacing it with", numeric_literal::format(&float_str, type_suffix, true), Applicability::MachineApplicable, - SuggestionStyle::ShowAlways, ); }, ); @@ -134,12 +133,11 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { expr.span, "float has excessive precision", |diag| { - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( expr.span, "consider changing the type or truncating it to", numeric_literal::format(&float_str, type_suffix, true), Applicability::MachineApplicable, - SuggestionStyle::ShowAlways, ); }, ); diff --git a/clippy_lints/src/functions/impl_trait_in_params.rs b/clippy_lints/src/functions/impl_trait_in_params.rs index cf85c74e688d2..05e341e06fde4 100644 --- a/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/clippy_lints/src/functions/impl_trait_in_params.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_test; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, GenericParam, Generics, HirId, ImplItem, ImplItemKind, TraitItem, TraitItemKind}; @@ -18,20 +19,18 @@ fn report(cx: &LateContext<'_>, param: &GenericParam<'_>, generics: &Generics<'_ |diag| { if let Some(gen_span) = generics.span_for_param_suggestion() { // If there's already a generic param with the same bound, do not lint **this** suggestion. - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( gen_span, "add a type parameter", format!(", {{ /* Generic name */ }}: {}", ¶m.name.ident().as_str()[5..]), - rustc_errors::Applicability::HasPlaceholders, - rustc_errors::SuggestionStyle::ShowAlways, + Applicability::HasPlaceholders, ); } else { - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( generics.span, "add a type parameter", format!("<{{ /* Generic name */ }}: {}>", ¶m.name.ident().as_str()[5..]), - rustc_errors::Applicability::HasPlaceholders, - rustc_errors::SuggestionStyle::ShowAlways, + Applicability::HasPlaceholders, ); } }, diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index b926e1e62ba0c..ba06567b9572c 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context, wal use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro}; use core::ops::ControlFlow; -use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -54,13 +54,7 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) { |diag| { let mut app = Applicability::MachineApplicable; let snip = snippet_with_applicability(cx, span, "..", &mut app); - diag.span_suggestion_with_style( - span, - "add `return` as shown", - format!("return {snip}"), - app, - SuggestionStyle::ShowAlways, - ); + diag.span_suggestion_verbose(span, "add `return` as shown", format!("return {snip}"), app); }, ); } @@ -75,12 +69,11 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp |diag| { let mut app = Applicability::MachineApplicable; let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0; - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( break_span, "change `break` to `return` as shown", format!("return {snip}"), app, - SuggestionStyle::ShowAlways, ); }, ); diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index 752e1326e3e05..ccab1e27d3b0c 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -3,7 +3,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::{get_parent_expr, is_from_proc_macro}; use hir::def_id::DefId; -use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{ExprKind, Item, ItemKind, QPath, UseKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -143,12 +143,11 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { && !is_from_proc_macro(cx, expr) { span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| { - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( span, "use the associated constant instead", sugg, Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, ); }); } diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index c6285c87a2636..9daad1a8a949e 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -74,7 +74,7 @@ pub(super) fn check<'tcx>( "&" }; - diag.span_suggestion_with_style( + diag.span_suggestion_verbose( span, "using `[]` is clearer and more concise", format!( @@ -82,7 +82,6 @@ pub(super) fn check<'tcx>( snippet_with_applicability(cx, recv.span, "..", &mut applicability) ), applicability, - rustc_errors::SuggestionStyle::ShowAlways, ); }, ); From ed46141081597395b5299f8c7aaeb489e0465407 Mon Sep 17 00:00:00 2001 From: Alex Macleod <alex@macleod.io> Date: Sat, 17 Aug 2024 18:10:46 +0000 Subject: [PATCH 40/54] Remove duplicate `type_diagnostic_name` function --- clippy_lints/src/eta_reduction.rs | 4 ++-- clippy_utils/src/ty.rs | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 5a7226d590c4d..a77d09c378afe 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::type_diagnostic_name; +use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id}; use rustc_errors::Applicability; @@ -139,7 +139,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc { let callee_ty_raw = typeck.expr_ty(callee); let callee_ty = callee_ty_raw.peel_refs(); - if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) + if matches!(get_type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) || !check_inputs(typeck, body.params, None, args) { return; diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index bd48990aea95f..a557a7f31ce80 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -455,11 +455,6 @@ pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: LangItem) } } -/// Gets the diagnostic name of the type, if it has one -pub fn type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> { - ty.ty_adt_def().and_then(|adt| cx.tcx.get_diagnostic_name(adt.did())) -} - /// Return `true` if the passed `typ` is `isize` or `usize`. pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) From 9aa656df9f6c68d14e613e56e3a6afac2644957e Mon Sep 17 00:00:00 2001 From: Alex Macleod <alex@macleod.io> Date: Sat, 17 Aug 2024 18:17:53 +0000 Subject: [PATCH 41/54] Remove redundant doc comments --- clippy_lints/src/attrs/mod.rs | 2 -- clippy_lints/src/cargo/common_metadata.rs | 2 -- .../src/cargo/multiple_crate_versions.rs | 2 -- clippy_lints/src/checked_conversions.rs | 2 -- clippy_lints/src/cognitive_complexity.rs | 2 -- clippy_lints/src/collapsible_if.rs | 14 -------- clippy_lints/src/else_if_without_else.rs | 2 -- clippy_lints/src/empty_enum.rs | 2 -- clippy_lints/src/enum_clike.rs | 3 -- clippy_lints/src/if_not_else.rs | 3 -- clippy_lints/src/indexing_slicing.rs | 2 -- clippy_lints/src/inherent_impl.rs | 2 -- clippy_lints/src/inline_fn_without_body.rs | 2 -- clippy_lints/src/int_plus_one.rs | 2 -- clippy_lints/src/item_name_repetitions.rs | 2 -- clippy_lints/src/items_after_statements.rs | 2 -- clippy_lints/src/large_enum_variant.rs | 2 -- clippy_lints/src/literal_representation.rs | 3 -- .../src/methods/is_digit_ascii_radix.rs | 2 -- clippy_lints/src/mutex_atomic.rs | 4 --- clippy_lints/src/needless_bool.rs | 4 --- clippy_lints/src/needless_continue.rs | 35 ------------------- clippy_lints/src/non_copy_const.rs | 4 --- clippy_lints/src/ptr.rs | 2 -- clippy_lints/src/size_of_in_element_count.rs | 3 -- clippy_lints/src/utils/author.rs | 3 -- 26 files changed, 108 deletions(-) diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 5d99001e5aa3f..3b14e9aee7fc0 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -1,5 +1,3 @@ -//! checks for attributes - mod allow_attributes; mod allow_attributes_without_reason; mod blanket_clippy_restriction_lints; diff --git a/clippy_lints/src/cargo/common_metadata.rs b/clippy_lints/src/cargo/common_metadata.rs index 3af2d8c025684..fed0aa8b2758b 100644 --- a/clippy_lints/src/cargo/common_metadata.rs +++ b/clippy_lints/src/cargo/common_metadata.rs @@ -1,5 +1,3 @@ -//! lint on missing cargo common metadata - use cargo_metadata::Metadata; use clippy_utils::diagnostics::span_lint; use rustc_lint::LateContext; diff --git a/clippy_lints/src/cargo/multiple_crate_versions.rs b/clippy_lints/src/cargo/multiple_crate_versions.rs index 2769463c8a53c..44cd1f7192fb1 100644 --- a/clippy_lints/src/cargo/multiple_crate_versions.rs +++ b/clippy_lints/src/cargo/multiple_crate_versions.rs @@ -1,5 +1,3 @@ -//! lint on multiple versions of a crate being used - use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId}; use clippy_utils::diagnostics::span_lint; use itertools::Itertools; diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 1711565fca891..dd7c34d1e4687 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -1,5 +1,3 @@ -//! lint on manually implemented checked conversions that could be transformed into `try_from` - use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 5fa0522e4e5f9..0099eefbc8dc1 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -1,5 +1,3 @@ -//! calculate cognitive complexity and warn about overly complex functions - use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{IntoSpan, SpanRangeExt}; diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index f311c052ad6ee..e73bfc6ebf7a1 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,17 +1,3 @@ -//! Checks for if expressions that contain only an if expression. -//! -//! For example, the lint would catch: -//! -//! ```rust,ignore -//! if x { -//! if y { -//! println!("Hello world"); -//! } -//! } -//! ``` -//! -//! This lint is **warn** by default - use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability}; use clippy_utils::sugg::Sugg; diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs index 02f9c2c36488d..5315f55ba3882 100644 --- a/clippy_lints/src/else_if_without_else.rs +++ b/clippy_lints/src/else_if_without_else.rs @@ -1,5 +1,3 @@ -//! Lint on if expressions with an else if, but without a final else branch. - use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index 1869faab1d326..f4c55738cb83a 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -1,5 +1,3 @@ -//! lint when there is an enum with no variants - use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index e54cd248ead29..4755cefe784c5 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -1,6 +1,3 @@ -//! lint on C-like enums that are `repr(isize/usize)` and have values that -//! don't fit into an `i32` - use clippy_utils::consts::{mir_to_const, Constant}; use clippy_utils::diagnostics::span_lint; use rustc_hir::{Item, ItemKind}; diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index 0ebd8d0c237b6..120c5396a1cd5 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -1,6 +1,3 @@ -//! lint on if branches that could be swapped so no `!` operation is necessary -//! on the condition - use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_else_clause; diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 3ac50b8f1fba5..22e9674714fe5 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -1,5 +1,3 @@ -//! lint on indexing and slicing operations - use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 9eed7aa924339..d39f910f9936d 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -1,5 +1,3 @@ -//! lint on inherent implementations - use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use rustc_data_structures::fx::FxHashMap; diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index 5657c58bb0a47..1b900f6be8e85 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -1,5 +1,3 @@ -//! checks for `#[inline]` on trait methods without bodies - use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::DiagExt; use rustc_errors::Applicability; diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index b8e0eef7c7e9e..941caaab1f1bf 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -1,5 +1,3 @@ -//! lint on blocks unnecessarily using >= with a + 1 or - 1 - use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind}; diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 4d44bae02b87d..74bf82f58bdcf 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -1,5 +1,3 @@ -//! lint on enum variants that are prefixed or suffixed by the same characters - use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir}; use clippy_utils::is_bool; diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index a88d8e24fda8d..4f066113aea33 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -1,5 +1,3 @@ -//! lint when items are used after statements - use clippy_utils::diagnostics::span_lint_hir; use rustc_hir::{Block, ItemKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 225d79aa71d68..2f027c1170728 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,5 +1,3 @@ -//! lint when there is a large size difference between variants on an enum - use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 259e4d6c08fb7..f51c593b2de9e 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -1,6 +1,3 @@ -//! Lints concerned with the grouping of digits with underscores in integral or -//! floating-point literal expressions. - use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index 22d896433f07a..40b48ccca5d5d 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -1,5 +1,3 @@ -//! Lint for `c.is_digit(10)` - use super::IS_DIGIT_ASCII_RADIX; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, FullInt}; diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 853e476a006c6..3884149645883 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,7 +1,3 @@ -//! Checks for usage of mutex where an atomic value could be used -//! -//! This lint is **allow** by default - use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::Expr; diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 9cb4fa41c73f1..df155a7a41257 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -1,7 +1,3 @@ -//! Checks for needless boolean results of if-else expressions -//! -//! This lint is **warn** by default - use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index b97cb4579ca7b..ce97370d4d9fa 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -1,38 +1,3 @@ -//! Checks for continue statements in loops that are redundant. -//! -//! For example, the lint would catch -//! -//! ```rust -//! let mut a = 1; -//! let x = true; -//! -//! while a < 5 { -//! a = 6; -//! if x { -//! // ... -//! } else { -//! continue; -//! } -//! println!("Hello, world"); -//! } -//! ``` -//! -//! And suggest something like this: -//! -//! ```rust -//! let mut a = 1; -//! let x = true; -//! -//! while a < 5 { -//! a = 6; -//! if x { -//! // ... -//! println!("Hello, world"); -//! } -//! } -//! ``` -//! -//! This lint is **warn** by default. use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{indent_of, snippet, snippet_block}; use rustc_ast::ast; diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 6915cd4061551..e50562d8e4973 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -1,7 +1,3 @@ -//! Checks for usage of const which the type is not `Freeze` (`Cell`-free). -//! -//! This lint is **warn** by default. - use std::ptr; use clippy_config::Conf; diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 1e824681fa12f..125f694996c1c 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -1,5 +1,3 @@ -//! Checks for usage of `&Vec[_]` and `&String`. - use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::expr_sig; diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 01f0e3cfadbd4..7750d8909d362 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -1,6 +1,3 @@ -//! Lint on use of `size_of` or `size_of_val` of T in an expression -//! expecting a count of T - use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 316c1f32d3a2a..0cce45290cfe7 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -1,6 +1,3 @@ -//! A group of attributes that can be attached to Rust code in order -//! to generate a clippy lint detecting said code automatically. - use clippy_utils::{get_attr, higher}; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_ast::LitIntType; From 6993752607f75165f6b2fd41e7d622648e1a313c Mon Sep 17 00:00:00 2001 From: Alex Macleod <alex@macleod.io> Date: Sat, 17 Aug 2024 18:42:21 +0000 Subject: [PATCH 42/54] Replace some iteration with `get_[type_]diagnostic_name` --- clippy_lints/src/collection_is_never_read.rs | 37 +++++++++----------- clippy_lints/src/functions/must_use.rs | 8 ++--- clippy_lints/src/infinite_iter.rs | 33 ++++++++--------- clippy_lints/src/manual_retain.rs | 34 ++++++++---------- clippy_lints/src/methods/needless_collect.rs | 9 ++--- clippy_lints/src/mut_key.rs | 14 ++++---- 6 files changed, 60 insertions(+), 75 deletions(-) diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index eebda3ff76fd9..c6847411c75ab 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; use clippy_utils::visitors::{for_each_expr, Visitable}; use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; @@ -7,7 +7,6 @@ use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; -use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -44,24 +43,11 @@ declare_clippy_lint! { } declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]); -// Add `String` here when it is added to diagnostic items -static COLLECTIONS: [Symbol; 9] = [ - sym::BTreeMap, - sym::BTreeSet, - sym::BinaryHeap, - sym::HashMap, - sym::HashSet, - sym::LinkedList, - sym::Option, - sym::Vec, - sym::VecDeque, -]; - impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { // Look for local variables whose type is a container. Search surrounding block for read access. if let PatKind::Binding(_, local_id, _, _) = local.pat.kind - && match_acceptable_type(cx, local, &COLLECTIONS) + && match_acceptable_type(cx, local) && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id) && has_no_read_access(cx, local_id, enclosing_block) { @@ -70,11 +56,22 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { } } -fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>, collections: &[Symbol]) -> bool { +fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { let ty = cx.typeck_results().pat_ty(local.pat); - collections.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym)) - // String type is a lang item but not a diagnostic item for now so we need a separate check - || is_type_lang_item(cx, ty, LangItem::String) + matches!( + get_type_diagnostic_name(cx, ty), + Some( + sym::BTreeMap + | sym::BTreeSet + | sym::BinaryHeap + | sym::HashMap + | sym::HashSet + | sym::LinkedList + | sym::Option + | sym::Vec + | sym::VecDeque + ) + ) || is_type_lang_item(cx, ty, LangItem::String) } fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirId, block: T) -> bool { diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index b179d7b52492c..b008ce6892100 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -8,7 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{sym, Span}; use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; @@ -193,17 +193,13 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) } } -static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc]; - fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) -> bool { match *ty.kind() { // primitive types are never mutable ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, ty::Adt(adt, args) => { tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env) - || KNOWN_WRAPPER_TYS - .iter() - .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did())) + || matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Rc | sym::Arc)) && args.types().any(|ty| is_mutable_ty(cx, ty, tys)) }, ty::Tuple(args) => args.iter().any(|ty| is_mutable_ty(cx, ty, tys)), diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 676d50c4951b1..71c317552ee1a 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::higher; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -210,18 +210,6 @@ const COMPLETING_METHODS: [(&str, usize); 12] = [ ("product", 0), ]; -/// the paths of types that are known to be infinitely allocating -const INFINITE_COLLECTORS: &[Symbol] = &[ - sym::BinaryHeap, - sym::BTreeMap, - sym::BTreeSet, - sym::HashMap, - sym::HashSet, - sym::LinkedList, - sym::Vec, - sym::VecDeque, -]; - fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { ExprKind::MethodCall(method, receiver, args, _) => { @@ -248,10 +236,19 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } } else if method.ident.name == sym!(collect) { let ty = cx.typeck_results().expr_ty(expr); - if INFINITE_COLLECTORS - .iter() - .any(|diag_item| is_type_diagnostic_item(cx, ty, *diag_item)) - { + if matches!( + get_type_diagnostic_name(cx, ty), + Some( + sym::BinaryHeap + | sym::BTreeMap + | sym::BTreeSet + | sym::HashMap + | sym::HashSet + | sym::LinkedList + | sym::Vec + | sym::VecDeque, + ) + ) { return is_infinite(cx, receiver); } } diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index d549470ed47e1..d4e53f8f74bd6 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -2,14 +2,14 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; use clippy_utils::{match_def_path, paths, SpanlessEq}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::ExprKind::Assign; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{impl_lint_pass, RustcVersion}; +use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; use rustc_span::Span; @@ -20,16 +20,6 @@ const ACCEPTABLE_METHODS: [&[&str]; 5] = [ &paths::SLICE_INTO, &paths::VEC_DEQUE_ITER, ]; -const ACCEPTABLE_TYPES: [(rustc_span::Symbol, Option<RustcVersion>); 7] = [ - (sym::BinaryHeap, Some(msrvs::BINARY_HEAP_RETAIN)), - (sym::BTreeSet, Some(msrvs::BTREE_SET_RETAIN)), - (sym::BTreeMap, Some(msrvs::BTREE_MAP_RETAIN)), - (sym::HashSet, Some(msrvs::HASH_SET_RETAIN)), - (sym::HashMap, Some(msrvs::HASH_MAP_RETAIN)), - (sym::Vec, None), - (sym::VecDeque, None), -]; -const MAP_TYPES: [rustc_span::Symbol; 2] = [sym::BTreeMap, sym::HashMap]; declare_clippy_lint! { /// ### What it does @@ -264,16 +254,22 @@ fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> boo } fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool { - let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); - ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| { - is_type_diagnostic_item(cx, expr_ty, *ty) - && acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv)) - }) + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + let required = match get_type_diagnostic_name(cx, ty) { + Some(sym::BinaryHeap) => msrvs::BINARY_HEAP_RETAIN, + Some(sym::BTreeSet) => msrvs::BTREE_SET_RETAIN, + Some(sym::BTreeMap) => msrvs::BTREE_MAP_RETAIN, + Some(sym::HashSet) => msrvs::HASH_SET_RETAIN, + Some(sym::HashMap) => msrvs::HASH_MAP_RETAIN, + Some(sym::Vec | sym::VecDeque) => return true, + _ => return false, + }; + msrv.meets(required) } fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); - MAP_TYPES.iter().any(|ty| is_type_diagnostic_item(cx, expr_ty, *ty)) + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + matches!(get_type_diagnostic_name(cx, ty), Some(sym::BTreeMap | sym::HashMap)) } fn make_span_lint_and_sugg(cx: &LateContext<'_>, span: Span, sugg: String) { diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 46b457daf7074..f61923e5bf569 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -2,7 +2,7 @@ use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection}; +use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection}; use clippy_utils::{ can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, path_to_local_id, CaptureKind, @@ -88,9 +88,10 @@ pub(super) fn check<'tcx>( Node::LetStmt(l) => { if let PatKind::Binding(BindingMode::NONE | BindingMode::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)) + && matches!( + get_type_diagnostic_name(cx, ty), + Some(sym::Vec | sym::VecDeque | sym::BinaryHeap | sym::LinkedList) + ) && 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)) diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 83af9979b9e36..f52b3a6a5a160 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -125,14 +125,12 @@ impl<'tcx> MutableKeyType<'tcx> { // generics (because the compiler cannot ensure immutability for unknown types). fn check_ty_(&mut self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { let ty = ty.peel_refs(); - if let ty::Adt(def, args) = ty.kind() { - let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet] - .iter() - .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did())); - if !is_keyed_type { - return; - } - + if let ty::Adt(def, args) = ty.kind() + && matches!( + cx.tcx.get_diagnostic_name(def.did()), + Some(sym::HashMap | sym::BTreeMap | sym::HashSet | sym::BTreeSet) + ) + { let subst_ty = args.type_at(0); if self.interior_mut.is_interior_mut_ty(cx, subst_ty) { span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); From a2033428c4ca9883a2b7cbbd1f167d481b0d240c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume1.gomez@gmail.com> Date: Sun, 18 Aug 2024 14:13:45 +0200 Subject: [PATCH 43/54] Do not emit `TOO_LONG_FIRST_DOC_PARAGRAPH` lint if item is generated from proc-macro and simplify code to emit lint --- .../src/doc/too_long_first_doc_paragraph.rs | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index a47cba64b28e5..7bb3bb12f2ca9 100644 --- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -3,7 +3,8 @@ use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::LateContext; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::source::snippet_opt; use super::TOO_LONG_FIRST_DOC_PARAGRAPH; @@ -63,32 +64,28 @@ pub(super) fn check( let &[first_span, .., last_span] = spans.as_slice() else { return; }; + if is_from_proc_macro(cx, item) { + return; + } - if should_suggest_empty_doc - && let Some(second_span) = spans.get(1) - && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi()) - && let Some(snippet) = snippet_opt(cx, new_span) - { - span_lint_and_then( - cx, - TOO_LONG_FIRST_DOC_PARAGRAPH, - first_span.with_hi(last_span.lo()), - "first doc comment paragraph is too long", - |diag| { + span_lint_and_then( + cx, + TOO_LONG_FIRST_DOC_PARAGRAPH, + first_span.with_hi(last_span.lo()), + "first doc comment paragraph is too long", + |diag| { + if should_suggest_empty_doc + && let Some(second_span) = spans.get(1) + && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi()) + && let Some(snippet) = snippet_opt(cx, new_span) + { diag.span_suggestion( new_span, "add an empty line", format!("{snippet}///\n"), Applicability::MachineApplicable, ); - }, - ); - return; - } - span_lint( - cx, - TOO_LONG_FIRST_DOC_PARAGRAPH, - first_span.with_hi(last_span.lo()), - "first doc comment paragraph is too long", + } + }, ); } From 4e6fc6fa052d3278a5bf11ba805d292f5a5ecb4a Mon Sep 17 00:00:00 2001 From: sobolevn <mail@sobolevn.me> Date: Sun, 18 Aug 2024 19:40:56 +0300 Subject: [PATCH 44/54] Improve `collapsible_match` error message syntax --- clippy_lints/src/matches/collapsible_match.rs | 2 +- tests/ui/collapsible_match.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 90cfdecc19936..b666e77c09ecb 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -96,7 +96,7 @@ fn check_arm<'tcx>( // 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")) + format!(", prefixed by `{}`:", snippet(cx, binding_span, "their field name")) } else { String::new() }; diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 01944baee79af..1da78b56239c7 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -223,7 +223,7 @@ help: the outer pattern can be modified to include the inner pattern LL | if let Issue9647::A { a, .. } = x { | ^ replace this binding LL | if let Some(u) = a { - | ^^^^^^^ with this pattern, prefixed by a: + | ^^^^^^^ with this pattern, prefixed by `a`: error: this `if let` can be collapsed into the outer `if let` --> tests/ui/collapsible_match.rs:292:9 From 43e1145c80d1c2f4e159885d382659ace3a1bc5d Mon Sep 17 00:00:00 2001 From: Ralf Jung <post@ralfj.de> Date: Mon, 12 Aug 2024 10:57:57 +0200 Subject: [PATCH 45/54] rename AddressOf -> RawBorrow inside the compiler --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 553af913ef9df..95fbf0b2ea20a 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -110,7 +110,7 @@ fn check_rvalue<'tcx>( ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), - Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { + Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { check_place(tcx, *place, span, body, msrv) }, Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body, msrv), From 1958e1acd46e791382219429cb7c38c97bef677d Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Tue, 20 Aug 2024 01:51:32 +0900 Subject: [PATCH 46/54] fix false position in explicit_iter_loop with msrv --- clippy_config/src/msrvs.rs | 1 + clippy_lints/src/loops/explicit_iter_loop.rs | 12 ++++++++++-- tests/ui/explicit_iter_loop.fixed | 12 ++++++++++++ tests/ui/explicit_iter_loop.rs | 12 ++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index 5b9707efd2634..0e8215aa8e3cd 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -18,6 +18,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,81,0 { LINT_REASONS_STABILIZATION } + 1,80,0 { BOX_INTO_ITER} 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index eea5f2a94ea60..b134af500f540 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -3,7 +3,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{ - implements_trait, implements_trait_with_env, is_copy, make_normalized_projection, + implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection, make_normalized_projection_with_regions, normalize_with_regions, }; use rustc_errors::Applicability; @@ -20,9 +20,10 @@ pub(super) fn check( msrv: &Msrv, enforce_iter_loop_reborrow: bool, ) { - let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow) else { + let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow, msrv) else { return; }; + if let ty::Array(_, count) = *ty.peel_refs().kind() { if !ty.is_ref() { if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) { @@ -109,6 +110,7 @@ fn is_ref_iterable<'tcx>( self_arg: &Expr<'_>, call_expr: &Expr<'_>, enforce_iter_loop_reborrow: bool, + msrv: &Msrv, ) -> Option<(AdjustKind, Ty<'tcx>)> { let typeck = cx.typeck_results(); if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) @@ -128,6 +130,12 @@ fn is_ref_iterable<'tcx>( let self_ty = typeck.expr_ty(self_arg); let self_is_copy = is_copy(cx, self_ty); + if !msrv.meets(msrvs::BOX_INTO_ITER) + && is_type_lang_item(cx, self_ty.peel_refs(), rustc_hir::LangItem::OwnedBox) + { + return None; + } + if adjustments.is_empty() && self_is_copy { // Exact type match, already checked earlier return Some((AdjustKind::None, self_ty)); diff --git a/tests/ui/explicit_iter_loop.fixed b/tests/ui/explicit_iter_loop.fixed index f38b34c5a7c47..1148f0f6c6a63 100644 --- a/tests/ui/explicit_iter_loop.fixed +++ b/tests/ui/explicit_iter_loop.fixed @@ -153,3 +153,15 @@ fn main() { let r = &x; for _ in r {} } + +#[clippy::msrv = "1.79"] +pub fn issue_13184() { + // https://github.com/rust-lang/rust-clippy/issues/13184 + // No need to fix, as IntoIterator for Box is valid starting from 1.80 + let mut values: Box<[u32]> = Box::new([1, 2]); + for _ in values.iter() {} + for _ in values.iter_mut() {} + + let rvalues = &values; + for _ in rvalues.iter() {} +} diff --git a/tests/ui/explicit_iter_loop.rs b/tests/ui/explicit_iter_loop.rs index 2e701ada5ac6d..4dda2f13e5b83 100644 --- a/tests/ui/explicit_iter_loop.rs +++ b/tests/ui/explicit_iter_loop.rs @@ -153,3 +153,15 @@ fn main() { let r = &x; for _ in r.iter() {} } + +#[clippy::msrv = "1.79"] +pub fn issue_13184() { + // https://github.com/rust-lang/rust-clippy/issues/13184 + // No need to fix, as IntoIterator for Box is valid starting from 1.80 + let mut values: Box<[u32]> = Box::new([1, 2]); + for _ in values.iter() {} + for _ in values.iter_mut() {} + + let rvalues = &values; + for _ in rvalues.iter() {} +} From 7b7cf440cb5bc8733de38aeca8d42f3747bd45e1 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk <alexsemenyuk88@gmail.com> Date: Sun, 18 Aug 2024 17:50:29 +0500 Subject: [PATCH 47/54] string_slice should detect on Cow --- clippy_lints/src/strings.rs | 2 +- tests/ui/string_slice.rs | 5 +++++ tests/ui/string_slice.stderr | 14 ++++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index cfc387886dc01..6ca4ca000e9b4 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -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(); + let e_ty = cx.typeck_results().expr_ty_adjusted(target).peel_refs(); if e_ty.is_str() || is_type_lang_item(cx, e_ty, LangItem::String) { span_lint( cx, diff --git a/tests/ui/string_slice.rs b/tests/ui/string_slice.rs index 440a86b104a3d..1d1911aaa1d91 100644 --- a/tests/ui/string_slice.rs +++ b/tests/ui/string_slice.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + #[warn(clippy::string_slice)] #[allow(clippy::no_effect)] @@ -11,4 +13,7 @@ fn main() { let s = String::from(m); &s[0..2]; //~^ ERROR: indexing into a string may panic if the index is within a UTF-8 character + let a = Cow::Borrowed("foo"); + &a[0..3]; + //~^ ERROR: indexing into a string may panic if the index is within a UTF-8 character } diff --git a/tests/ui/string_slice.stderr b/tests/ui/string_slice.stderr index 7a4596b5f2d25..bc0fcde34b826 100644 --- a/tests/ui/string_slice.stderr +++ b/tests/ui/string_slice.stderr @@ -1,5 +1,5 @@ error: indexing into a string may panic if the index is within a UTF-8 character - --> tests/ui/string_slice.rs:5:6 + --> tests/ui/string_slice.rs:7:6 | LL | &"Ölkanne"[1..]; | ^^^^^^^^^^^^^^ @@ -8,16 +8,22 @@ LL | &"Ölkanne"[1..]; = help: to override `-D warnings` add `#[allow(clippy::string_slice)]` error: indexing into a string may panic if the index is within a UTF-8 character - --> tests/ui/string_slice.rs:9:6 + --> tests/ui/string_slice.rs:11:6 | LL | &m[2..5]; | ^^^^^^^ error: indexing into a string may panic if the index is within a UTF-8 character - --> tests/ui/string_slice.rs:12:6 + --> tests/ui/string_slice.rs:14:6 | LL | &s[0..2]; | ^^^^^^^ -error: aborting due to 3 previous errors +error: indexing into a string may panic if the index is within a UTF-8 character + --> tests/ui/string_slice.rs:17:6 + | +LL | &a[0..3]; + | ^^^^^^^ + +error: aborting due to 4 previous errors From fc8a025f171a4cf8e5839c2bd5fa33c25357e2a0 Mon Sep 17 00:00:00 2001 From: Milo Moisson <milomoisson@gmail.com> Date: Tue, 20 Aug 2024 12:04:28 +0200 Subject: [PATCH 48/54] fix(cfg_not_test): lint description typo --- clippy_lints/src/cfg_not_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/cfg_not_test.rs b/clippy_lints/src/cfg_not_test.rs index b54f392bf2fad..d820c1e0720b8 100644 --- a/clippy_lints/src/cfg_not_test.rs +++ b/clippy_lints/src/cfg_not_test.rs @@ -5,7 +5,7 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for usage of `cfg` that excludes code from `test` builds. (i.e., `#{cfg(not(test))]`) + /// Checks for usage of `cfg` that excludes code from `test` builds. (i.e., `#[cfg(not(test))]`) /// /// ### Why is this bad? /// This may give the false impression that a codebase has 100% coverage, yet actually has untested code. From 687d4e3d16d946a7b2a721db18a357cfc877422e Mon Sep 17 00:00:00 2001 From: Jason Newcomb <jsnewcomb@pm.me> Date: Tue, 20 Aug 2024 11:52:47 -0400 Subject: [PATCH 49/54] `declare_interior_mutable_const`: Ignore pointer types. --- clippy_lints/src/non_copy_const.rs | 2 +- .../ui/declare_interior_mutable_const/others.rs | 17 +++++++++++++++++ .../others.stderr | 10 +++++----- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index e50562d8e4973..25f547f1a43c0 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -184,7 +184,7 @@ impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTE impl<'tcx> NonCopyConst<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { Self { - interior_mut: InteriorMut::new(tcx, &conf.ignore_interior_mutability), + interior_mut: InteriorMut::without_pointers(tcx, &conf.ignore_interior_mutability), } } diff --git a/tests/ui/declare_interior_mutable_const/others.rs b/tests/ui/declare_interior_mutable_const/others.rs index 56a8d22cb1c81..9dafad8b784b9 100644 --- a/tests/ui/declare_interior_mutable_const/others.rs +++ b/tests/ui/declare_interior_mutable_const/others.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use std::cell::Cell; use std::fmt::Display; +use std::ptr; use std::sync::atomic::AtomicUsize; use std::sync::Once; @@ -53,4 +54,20 @@ mod issue_8493 { issue_8493!(); } +#[repr(C, align(8))] +struct NoAtomic(usize); +#[repr(C, align(8))] +struct WithAtomic(AtomicUsize); + +const fn with_non_null() -> *const WithAtomic { + const NO_ATOMIC: NoAtomic = NoAtomic(0); + (&NO_ATOMIC as *const NoAtomic).cast() +} +const WITH_ATOMIC: *const WithAtomic = with_non_null(); + +struct Generic<T>(T); +impl<T> Generic<T> { + const RAW_POINTER: *const Cell<T> = ptr::null(); +} + fn main() {} diff --git a/tests/ui/declare_interior_mutable_const/others.stderr b/tests/ui/declare_interior_mutable_const/others.stderr index 1f2b9561ce509..4a7251471424e 100644 --- a/tests/ui/declare_interior_mutable_const/others.stderr +++ b/tests/ui/declare_interior_mutable_const/others.stderr @@ -1,5 +1,5 @@ error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:9:1 + --> tests/ui/declare_interior_mutable_const/others.rs:10:1 | LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:10:1 + --> tests/ui/declare_interior_mutable_const/others.rs:11:1 | LL | const CELL: Cell<usize> = Cell::new(6); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | const CELL: Cell<usize> = Cell::new(6); = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:11:1 + --> tests/ui/declare_interior_mutable_const/others.rs:12:1 | LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], V = help: consider making this a static item error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:16:9 + --> tests/ui/declare_interior_mutable_const/others.rs:17:9 | LL | const $name: $ty = $e; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | declare_const!(_ONCE: Once = Once::new()); = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:44:13 + --> tests/ui/declare_interior_mutable_const/others.rs:45:13 | LL | const _BAZ: Cell<usize> = Cell::new(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 3f97261fbaff449c86c1ff97d0c342a567527c98 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener <bruce.mitchener@gmail.com> Date: Wed, 21 Aug 2024 21:42:57 +0700 Subject: [PATCH 50/54] Fix a couple of typos. --- clippy_lints/src/returns.rs | 2 +- clippy_lints/src/use_self.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index ffc9475cf9cdf..5ce091b7a7a30 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -399,7 +399,7 @@ fn check_final_expr<'tcx>( // Returns may be used to turn an expression into a statement in rustc's AST. // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) - // `#[expect(clippy::needless_return)]` needs to be handled separatly to + // `#[expect(clippy::needless_return)]` needs to be handled separately to // actually fulfill the expectation (clippy::#12998) match cx.tcx.hir().attrs(expr.hir_id) { [] => {}, diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 93785b45c27d4..5da48f4f7fbd7 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -229,7 +229,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity() && same_type_and_consts(ty, impl_ty) // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that - // the lifetime parameters of `ty` are ellided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in + // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in // which case we must still trigger the lint. && (has_no_lifetime(ty) || same_lifetimes(ty, impl_ty)) { From 8a4c34a5b7f084f64ab20a4120c61521a1a2b662 Mon Sep 17 00:00:00 2001 From: Jason Newcomb <jsnewcomb@pm.me> Date: Fri, 9 Aug 2024 09:04:18 -0400 Subject: [PATCH 51/54] Start removing `snippet_opt` --- clippy_lints/src/booleans.rs | 33 +++++++------ clippy_lints/src/borrow_deref_ref.rs | 7 +-- clippy_lints/src/format.rs | 4 +- clippy_lints/src/format_args.rs | 4 +- clippy_lints/src/from_over_into.rs | 12 ++--- clippy_lints/src/int_plus_one.rs | 6 +-- clippy_lints/src/items_after_test_module.rs | 9 ++-- clippy_lints/src/large_stack_frames.rs | 4 +- clippy_lints/src/len_zero.rs | 4 +- clippy_lints/src/literal_representation.rs | 6 +-- clippy_lints/src/manual_float_methods.rs | 4 +- clippy_lints/src/manual_hash_one.rs | 6 +-- clippy_lints/src/manual_non_exhaustive.rs | 6 +-- clippy_lints/src/manual_range_patterns.rs | 6 +-- .../src/methods/unnecessary_iter_cloned.rs | 8 +-- .../src/misc_early/unneeded_field_pattern.rs | 27 +++++----- .../src/missing_enforced_import_rename.rs | 4 +- clippy_lints/src/needless_if.rs | 4 +- clippy_lints/src/needless_late_init.rs | 11 +++-- clippy_lints/src/no_effect.rs | 47 +++++++++--------- clippy_lints/src/nonstandard_macro_braces.rs | 6 +-- .../src/operators/assign_op_pattern.rs | 6 +-- .../src/operators/misrefactored_assign_op.rs | 6 ++- .../src/operators/needless_bitwise_bool.rs | 6 +-- clippy_lints/src/operators/ptr_eq.rs | 6 +-- clippy_lints/src/unit_types/unit_arg.rs | 20 ++++---- .../src/utils/format_args_collector.rs | 49 ++++++------------- clippy_utils/src/attrs.rs | 38 +++++++------- clippy_utils/src/hir_utils.rs | 18 +++---- 29 files changed, 177 insertions(+), 190 deletions(-) diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index a2f48c18170a6..e933fdf1d6e6e 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,6 +1,6 @@ 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::source::SpanRangeExt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -134,28 +134,30 @@ fn check_inverted_bool_in_condition( let suggestion = match (left.kind, right.kind) { (ExprKind::Unary(UnOp::Not, left_sub), ExprKind::Unary(UnOp::Not, right_sub)) => { - let Some(left) = snippet_opt(cx, left_sub.span) else { + let Some(left) = left_sub.span.get_source_text(cx) else { return; }; - let Some(right) = snippet_opt(cx, right_sub.span) else { + let Some(right) = right_sub.span.get_source_text(cx) else { return; }; let Some(op) = bin_op_eq_str(op) else { return }; format!("{left} {op} {right}") }, (ExprKind::Unary(UnOp::Not, left_sub), _) => { - let Some(left) = snippet_opt(cx, left_sub.span) else { + let Some(left) = left_sub.span.get_source_text(cx) else { return; }; - let Some(right) = snippet_opt(cx, right.span) else { + let Some(right) = right.span.get_source_text(cx) else { return; }; let Some(op) = inverted_bin_op_eq_str(op) else { return }; format!("{left} {op} {right}") }, (_, ExprKind::Unary(UnOp::Not, right_sub)) => { - let Some(left) = snippet_opt(cx, left.span) else { return }; - let Some(right) = snippet_opt(cx, right_sub.span) else { + let Some(left) = left.span.get_source_text(cx) else { + return; + }; + let Some(right) = right_sub.span.get_source_text(cx) else { return; }; let Some(op) = inverted_bin_op_eq_str(op) else { return }; @@ -313,8 +315,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> { self.output.push_str(&str); } else { self.output.push('!'); - let snip = snippet_opt(self.cx, terminal.span)?; - self.output.push_str(&snip); + self.output.push_str(&terminal.span.get_source_text(self.cx)?); } }, True | False | Not(_) => { @@ -345,8 +346,12 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> { } }, &Term(n) => { - let snip = snippet_opt(self.cx, self.terminals[n as usize].span.source_callsite())?; - self.output.push_str(&snip); + self.output.push_str( + &self.terminals[n as usize] + .span + .source_callsite() + .get_source_text(self.cx)?, + ); }, } Some(()) @@ -370,8 +375,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> { _ => None, } .and_then(|op| { - let lhs_snippet = snippet_opt(cx, lhs.span)?; - let rhs_snippet = snippet_opt(cx, rhs.span)?; + let lhs_snippet = lhs.span.get_source_text(cx)?; + let rhs_snippet = rhs.span.get_source_text(cx)?; if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) { if let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) { @@ -399,7 +404,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> { let path: &str = path.ident.name.as_str(); a == path }) - .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", snippet_opt(cx, receiver.span)?))) + .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", receiver.span.get_source_text(cx)?))) }, _ => None, } diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index bd123a725a736..cba8224b84c9e 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -1,6 +1,6 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed}; use rustc_errors::Applicability; @@ -73,6 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { } }) && !is_from_proc_macro(cx, e) + && let Some(deref_text) = deref_target.span.get_source_text(cx) { span_lint_and_then( cx, @@ -83,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { diag.span_suggestion( e.span, "if you would like to reborrow, try removing `&*`", - snippet_opt(cx, deref_target.span).unwrap(), + deref_text.as_str(), Applicability::MachineApplicable, ); @@ -98,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { diag.span_suggestion( e.span, "if you would like to deref, try using `&**`", - format!("&**{}", &snippet_opt(cx, deref_target.span).unwrap()), + format!("&**{deref_text}"), Applicability::MaybeIncorrect, ); }, diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index c14bd38a1f21c..4dedff69b9abd 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage}; -use clippy_utils::source::{snippet_opt, snippet_with_context}; +use clippy_utils::source::{snippet_with_context, SpanRangeExt}; use clippy_utils::sugg::Sugg; use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait}; use rustc_errors::Applicability; @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { ([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability), ([], [_]) => { // Simulate macro expansion, converting {{ and }} to { and }. - let Some(snippet) = snippet_opt(cx, format_args.span) else { + let Some(snippet) = format_args.span.get_source_text(cx) else { return; }; let s_expand = snippet.replace("{{", "{").replace("}}", "}"); diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 823992767e508..020c0c5440d93 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -7,7 +7,7 @@ use clippy_utils::macros::{ find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall, }; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{implements_trait, is_type_lang_item}; use itertools::Itertools; use rustc_ast::{ @@ -407,7 +407,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> { count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()) && implements_trait(cx, target, display_trait_id, &[]) && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait() - && let Some(receiver_snippet) = snippet_opt(cx, receiver.span.source_callsite()) + && let Some(receiver_snippet) = receiver.span.source_callsite().get_source_text(cx) { let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); if n_needed_derefs == 0 && !needs_ref { diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index b6c8437fb8f73..5e9098474dc02 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -3,7 +3,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::path_def_id; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_path, Visitor}; use rustc_hir::{ @@ -178,8 +178,8 @@ fn convert_to_from( return None; }; - let from = snippet_opt(cx, self_ty.span)?; - let into = snippet_opt(cx, target_ty.span)?; + let from = self_ty.span.get_source_text(cx)?; + let into = target_ty.span.get_source_text(cx)?; let return_type = matches!(sig.decl.output, FnRetTy::Return(_)) .then_some(String::from("Self")) @@ -190,10 +190,10 @@ fn convert_to_from( (into_trait_seg.ident.span, String::from("From")), // impl Into<T> for U -> impl Into<U> for U // ~ ~ - (target_ty.span, from.clone()), + (target_ty.span, from.to_owned()), // impl Into<T> for U -> impl Into<T> for T // ~ ~ - (self_ty.span, into), + (self_ty.span, into.to_owned()), // fn into(self) -> T -> fn from(self) -> T // ~~~~ ~~~~ (impl_item.ident.span, String::from("from")), @@ -223,7 +223,7 @@ fn convert_to_from( } for span in finder.upper { - suggestions.push((span, from.clone())); + suggestions.push((span, from.to_owned())); } for span in finder.lower { suggestions.push((span, String::from("val"))); diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 941caaab1f1bf..fc575bff7e63f 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind}; use rustc_ast::token; use rustc_errors::Applicability; @@ -130,8 +130,8 @@ impl IntPlusOne { BinOpKind::Le => "<", _ => return None, }; - if let Some(snippet) = snippet_opt(cx, node.span) { - if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) { + if let Some(snippet) = node.span.get_source_text(cx) { + if let Some(other_side_snippet) = other_side.span.get_source_text(cx) { let rec = match side { Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")), Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")), diff --git a/clippy_lints/src/items_after_test_module.rs b/clippy_lints/src/items_after_test_module.rs index 3614fb8cc963b..8caa484bb9934 100644 --- a/clippy_lints/src/items_after_test_module.rs +++ b/clippy_lints/src/items_after_test_module.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro}; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::{HirId, Item, ItemKind, Mod}; @@ -93,11 +93,14 @@ impl LateLintPass<'_> for ItemsAfterTestModule { if let Some(prev) = mod_pos.checked_sub(1) && let prev = cx.tcx.hir().item(module.item_ids[prev]) && let items_span = last.span.with_lo(test_mod.span.hi()) - && let Some(items) = snippet_opt(cx, items_span) + && let Some(items) = items_span.get_source_text(cx) { diag.multipart_suggestion_with_style( "move the items to before the test module was defined", - vec![(prev.span.shrink_to_hi(), items), (items_span, String::new())], + vec![ + (prev.span.shrink_to_hi(), items.to_owned()), + (items_span, String::new()), + ], Applicability::MachineApplicable, SuggestionStyle::HideCodeAlways, ); diff --git a/clippy_lints/src/large_stack_frames.rs b/clippy_lints/src/large_stack_frames.rs index 4abf7edc9b4e5..d2bdf194adad9 100644 --- a/clippy_lints/src/large_stack_frames.rs +++ b/clippy_lints/src/large_stack_frames.rs @@ -3,7 +3,7 @@ use std::{fmt, ops}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::fn_has_unsatisfiable_preds; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl}; @@ -186,7 +186,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { // TODO: Is there a cleaner, robust way to ask this question? // The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data", // and that doesn't get us the true name in scope rather than the span text either. - if let Some(name) = snippet_opt(cx, local_span) + if let Some(name) = local_span.get_source_text(cx) && is_ident(&name) { // If the local is an ordinary named variable, diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 4c737371bd238..0bc7fc2dd9d1e 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet_opt, snippet_with_context}; +use clippy_utils::source::{snippet_with_context, SpanRangeExt}; use clippy_utils::sugg::{has_enclosing_paren, Sugg}; use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; use rustc_ast::ast::LitKind; @@ -216,7 +216,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { } fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span { - let Some(snippet) = snippet_opt(cx, span) else { + let Some(snippet) = span.get_source_text(cx) else { return span; }; if has_enclosing_paren(snippet) { diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index f51c593b2de9e..81f2a03fb55fb 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_ast::token; use rustc_errors::Applicability; @@ -225,7 +225,7 @@ impl LiteralDigitGrouping { } fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { - if let Some(src) = snippet_opt(cx, span) + if let Some(src) = span.get_source_text(cx) && let Ok(lit_kind) = LitKind::from_token_lit(lit) && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) { @@ -439,7 +439,7 @@ impl DecimalLiteralRepresentation { // Lint integral literals. if let Ok(lit_kind) = LitKind::from_token_lit(lit) && let LitKind::Int(val, _) = lit_kind - && let Some(src) = snippet_opt(cx, span) + && let Some(src) = span.get_source_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) && num_lit.radix == Radix::Decimal && val >= u128::from(self.threshold) diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 6bdc79129a9bc..9d3ddab60bb3a 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::{is_from_proc_macro, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Constness, Expr, ExprKind}; @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { // case somebody does that for some reason && (is_infinity(&const_1) && is_neg_infinity(&const_2) || is_neg_infinity(&const_1) && is_infinity(&const_2)) - && let Some(local_snippet) = snippet_opt(cx, first.span) + && let Some(local_snippet) = first.span.get_source_text(cx) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite, diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index 1c568b1b74f32..11716a539ab5c 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -1,7 +1,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::visitors::{is_local_used, local_used_once}; use clippy_utils::{is_trait_method, path_to_local_id}; use rustc_errors::Applicability; @@ -107,8 +107,8 @@ impl LateLintPass<'_> for ManualHashOne { finish_expr.span, "manual implementation of `BuildHasher::hash_one`", |diag| { - if let Some(build_hasher) = snippet_opt(cx, build_hasher.span) - && let Some(hashed_value) = snippet_opt(cx, hashed_value.span) + if let Some(build_hasher) = build_hasher.span.get_source_text(cx) + && let Some(hashed_value) = hashed_value.span.get_source_text(cx) { diag.multipart_suggestion( "try", diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 28cfe22835af6..6b1d90483ccd1 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -2,7 +2,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::is_doc_hidden; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{self, VisibilityKind}; use rustc_ast::attr; use rustc_data_structures::fx::FxHashSet; @@ -124,7 +124,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { |diag| { if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)) && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter) - && let Some(snippet) = snippet_opt(cx, header_span) + && let Some(snippet) = header_span.get_source_text(cx) { diag.span_suggestion( header_span, @@ -194,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { "this seems like a manual implementation of the non-exhaustive pattern", |diag| { let header_span = cx.sess().source_map().span_until_char(enum_span, '{'); - if let Some(snippet) = snippet_opt(cx, header_span) { + if let Some(snippet) = header_span.get_source_text(cx) { diag.span_suggestion( header_span, "add the attribute", diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs index 07d4abbf5cd11..9afb2b5bc84cd 100644 --- a/clippy_lints/src/manual_range_patterns.rs +++ b/clippy_lints/src/manual_range_patterns.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -143,8 +143,8 @@ impl LateLintPass<'_> for ManualRangePatterns { pat.span, "this OR pattern can be rewritten using a range", |diag| { - if let Some(min) = snippet_opt(cx, min.span) - && let Some(max) = snippet_opt(cx, max.span) + if let Some(min) = min.span.get_source_text(cx) + && let Some(max) = max.span.get_source_text(cx) { diag.span_suggestion( pat.span, diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 70885e46e955e..4d18bc7ac7777 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -1,7 +1,7 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local}; @@ -40,7 +40,7 @@ pub fn check_for_loop_iter( && let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent) && let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body) && !clone_or_copy_needed - && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + && let Some(receiver_snippet) = receiver.span.get_source_text(cx) { // Issue 12098 // https://github.com/rust-lang/rust-clippy/issues/12098 @@ -100,7 +100,7 @@ pub fn check_for_loop_iter( && implements_trait(cx, collection_ty, into_iterator_trait_id, &[]) && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item") && iter_item_ty == into_iter_item_ty - && let Some(collection_snippet) = snippet_opt(cx, collection.span) + && let Some(collection_snippet) = collection.span.get_source_text(cx) { collection_snippet } else { @@ -122,7 +122,7 @@ pub fn check_for_loop_iter( } else { Applicability::MachineApplicable }; - diag.span_suggestion(expr.span, "use", snippet, applicability); + diag.span_suggestion(expr.span, "use", snippet.to_owned(), applicability); if !references_to_binding.is_empty() { diag.multipart_suggestion( "remove any references to the binding", diff --git a/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/clippy_lints/src/misc_early/unneeded_field_pattern.rs index 6db03adf44ac9..33ab94e00a5c6 100644 --- a/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; +use itertools::Itertools; use rustc_ast::ast::{Pat, PatKind}; use rustc_lint::EarlyContext; @@ -45,19 +46,6 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { "you matched a field with a wildcard pattern, consider using `..` instead", ); } else { - let mut normal = vec![]; - - for field in pfields { - match field.pat.kind { - PatKind::Wild => {}, - _ => { - if let Some(n) = snippet_opt(cx, field.span) { - normal.push(n); - } - }, - } - } - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( cx, @@ -65,7 +53,16 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { field.span, "you matched a field with a wildcard pattern, consider using `..` instead", |diag| { - diag.help(format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", "))); + diag.help(format!( + "try with `{type_name} {{ {}, .. }}`", + pfields + .iter() + .filter_map(|f| match f.pat.kind { + PatKind::Wild => None, + _ => f.span.get_source_text(cx), + }) + .format(", "), + )); }, ); } diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index cdf6645b3df77..59f29d242abf3 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::def_path_def_ids; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdMap; @@ -73,7 +73,7 @@ impl LateLintPass<'_> for ImportRename { && let Some(name) = self.renames.get(&id) // Remove semicolon since it is not present for nested imports && let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';') - && let Some(snip) = snippet_opt(cx, span_without_semi) + && let Some(snip) = span_without_semi.get_source_text(cx) && let Some(import) = match snip.split_once(" as ") { None => Some(snip.as_str()), Some((import, rename)) => { diff --git a/clippy_lints/src/needless_if.rs b/clippy_lints/src/needless_if.rs index 1d6233d432a75..8e14fbf2f8061 100644 --- a/clippy_lints/src/needless_if.rs +++ b/clippy_lints/src/needless_if.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::If; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::{snippet_opt, SpanRangeExt}; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -57,7 +57,7 @@ impl LateLintPass<'_> for NeedlessIf { src.bytes() .all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace()) }) - && let Some(cond_snippet) = snippet_opt(cx, cond.span) + && let Some(cond_snippet) = cond.span.get_source_text(cx) && !is_from_proc_macro(cx, expr) { span_lint_and_sugg( diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 4bfc30fa5cf80..46cdb82130f97 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{SourceText, SpanRangeExt}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; use core::ops::ControlFlow; @@ -236,7 +236,7 @@ fn first_usage<'tcx>( }) } -fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option<String> { +fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option<SourceText> { let span = local.span.with_hi(match local.ty { // let <pat>: <ty>; // ~~~~~~~~~~~~~~~ @@ -246,7 +246,7 @@ fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> None => local.pat.span.hi(), }); - snippet_opt(cx, span) + span.get_source_text(cx) } fn check<'tcx>( @@ -275,7 +275,10 @@ fn check<'tcx>( |diag| { diag.multipart_suggestion( format!("move the declaration `{binding_name}` here"), - vec![(local_stmt.span, String::new()), (assign.lhs_span, let_snippet)], + vec![ + (local_stmt.span, String::new()), + (assign.lhs_span, let_snippet.to_owned()), + ], Applicability::MachineApplicable, ); }, diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 8232e69db3917..b181791699a5b 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::has_drop; use clippy_utils::{ in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks, @@ -268,34 +268,31 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { && reduced.iter().all(|e| e.span.ctxt() == ctxt) { if let ExprKind::Index(..) = &expr.kind { - if is_inside_always_const_context(cx.tcx, expr.hir_id) { - return; + if !is_inside_always_const_context(cx.tcx, expr.hir_id) + && let [arr, func] = &*reduced + && let Some(arr) = arr.span.get_source_text(cx) + && let Some(func) = func.span.get_source_text(cx) + { + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be written as", + format!("assert!({arr}.len() > {func});"), + Applicability::MaybeIncorrect, + ); + }, + ); } - let snippet = - if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) { - format!("assert!({arr}.len() > {func});") - } else { - return; - }; - span_lint_hir_and_then( - cx, - UNNECESSARY_OPERATION, - expr.hir_id, - stmt.span, - "unnecessary operation", - |diag| { - diag.span_suggestion( - stmt.span, - "statement can be written as", - snippet, - Applicability::MaybeIncorrect, - ); - }, - ); } else { let mut snippet = String::new(); for e in reduced { - if let Some(snip) = snippet_opt(cx, e.span) { + if let Some(snip) = e.span.get_source_text(cx) { snippet.push_str(&snip); snippet.push(';'); } else { diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 2aaf1e6ff46da..51ba29d7389b9 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,7 +1,7 @@ use clippy_config::types::MacroMatcher; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{SourceText, SpanRangeExt}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; @@ -34,7 +34,7 @@ declare_clippy_lint! { } /// The (callsite span, (open brace, close brace), source snippet) -type MacroInfo = (Span, (char, char), String); +type MacroInfo = (Span, (char, char), SourceText); pub struct MacroBraces { macro_braces: FxHashMap<String, (char, char)>, @@ -94,7 +94,7 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind && let name = mac_name.as_str() && let Some(&braces) = mac_braces.macro_braces.get(name) - && let Some(snip) = snippet_opt(cx, span_call_site) + && let Some(snip) = span_call_site.get_source_text(cx) // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 && snip.starts_with(&format!("{name}!")) diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 641d881d974b8..11c97b4ef0073 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method}; @@ -46,8 +46,8 @@ pub(super) fn check<'tcx>( expr.span, "manual implementation of an assign operation", |diag| { - if let (Some(snip_a), Some(snip_r)) = - (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span)) + if let Some(snip_a) = assignee.span.get_source_text(cx) + && let Some(snip_r) = rhs.span.get_source_text(cx) { diag.span_suggestion( expr.span, diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index 311cbd050a10c..8daedd1c90140 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::{eq_expr_value, sugg}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -42,7 +42,9 @@ fn lint_misrefactored_assign_op( expr.span, "variable appears on both sides of an assignment operation", |diag| { - if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) { + if let Some(snip_a) = assignee.span.get_source_text(cx) + && let Some(snip_r) = rhs_other.span.get_source_text(cx) + { let a = &sugg::Sugg::hir(cx, assignee, ".."); let r = &sugg::Sugg::hir(cx, rhs, ".."); let long = format!("{snip_a} = {}", sugg::make_binop(op, a, r)); diff --git a/clippy_lints/src/operators/needless_bitwise_bool.rs b/clippy_lints/src/operators/needless_bitwise_bool.rs index ab5fb1787004e..9d8e833ef6d75 100644 --- a/clippy_lints/src/operators/needless_bitwise_bool.rs +++ b/clippy_lints/src/operators/needless_bitwise_bool.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -24,8 +24,8 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Exp e.span, "use of bitwise operator instead of lazy operator between booleans", |diag| { - if let Some(lhs_snip) = snippet_opt(cx, lhs.span) - && let Some(rhs_snip) = snippet_opt(cx, rhs.span) + if let Some(lhs_snip) = lhs.span.get_source_text(cx) + && let Some(rhs_snip) = rhs.span.get_source_text(cx) { let sugg = format!("{lhs_snip} {op_str} {rhs_snip}"); diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); diff --git a/clippy_lints/src/operators/ptr_eq.rs b/clippy_lints/src/operators/ptr_eq.rs index 607930561e0fc..861564d54569e 100644 --- a/clippy_lints/src/operators/ptr_eq.rs +++ b/clippy_lints/src/operators/ptr_eq.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::std_or_core; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -22,8 +22,8 @@ pub(super) fn check<'tcx>( if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left) && let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right) - && let Some(left_snip) = snippet_opt(cx, left_var.span) - && let Some(right_snip) = snippet_opt(cx, right_var.span) + && let Some(left_snip) = left_var.span.get_source_text(cx) + && let Some(right_snip) = right_var.span.get_source_text(cx) { let Some(top_crate) = std_or_core(cx) else { return }; span_lint_and_sugg( diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index afc53e6f32d96..978d49f09c3a8 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; +use clippy_utils::source::{indent_of, reindent_multiline, SourceText, SpanRangeExt}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, MatchSource, Node, StmtKind}; use rustc_lint::LateContext; @@ -79,7 +79,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp && block.expr.is_none() && let Some(last_stmt) = block.stmts.iter().last() && let StmtKind::Semi(last_expr) = last_stmt.kind - && let Some(snip) = snippet_opt(cx, last_expr.span) + && let Some(snip) = last_expr.span.get_source_text(cx) { Some((last_stmt.span, snip)) } else { @@ -90,24 +90,24 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp db.span_suggestion( span, "remove the semicolon from the last statement in the block", - sugg, + sugg.as_str(), Applicability::MaybeIncorrect, ); or = "or "; applicability = Applicability::MaybeIncorrect; }); - let arg_snippets: Vec<String> = args_to_recover + let arg_snippets: Vec<_> = args_to_recover .iter() - .filter_map(|arg| snippet_opt(cx, arg.span)) + .filter_map(|arg| arg.span.get_source_text(cx)) .collect(); - let arg_snippets_without_empty_blocks: Vec<String> = args_to_recover + let arg_snippets_without_empty_blocks: Vec<_> = args_to_recover .iter() .filter(|arg| !is_empty_block(arg)) - .filter_map(|arg| snippet_opt(cx, arg.span)) + .filter_map(|arg| arg.span.get_source_text(cx)) .collect(); - if let Some(call_snippet) = snippet_opt(cx, expr.span) { + if let Some(call_snippet) = expr.span.get_source_text(cx) { let sugg = fmt_stmts_and_call( cx, expr, @@ -161,8 +161,8 @@ fn fmt_stmts_and_call( cx: &LateContext<'_>, call_expr: &Expr<'_>, call_snippet: &str, - args_snippets: &[impl AsRef<str>], - non_empty_block_args_snippets: &[impl AsRef<str>], + args_snippets: &[SourceText], + non_empty_block_args_snippets: &[SourceText], ) -> String { let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0); let call_snippet_with_replacements = args_snippets diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs index 5acfd35fd6ae6..f50ce6c99deac 100644 --- a/clippy_lints/src/utils/format_args_collector.rs +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -1,5 +1,5 @@ use clippy_utils::macros::FormatArgsStorage; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use itertools::Itertools; use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; use rustc_data_structures::fx::FxHashMap; @@ -75,38 +75,21 @@ fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool { // `format!("{} {} {c}", "one", "two", c = "three")` // ^^ ^^ ^^^^^^ - let between_spans = once(args.span) + !once(args.span) .chain(argument_span) .tuple_windows() - .map(|(start, end)| start.between(end)); - - for between_span in between_spans { - let mut seen_comma = false; - - let Some(snippet) = snippet_opt(cx, between_span) else { - return true; - }; - for token in tokenize(&snippet) { - match token.kind { - TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {}, - TokenKind::Comma if !seen_comma => seen_comma = true, - // named arguments, `start_val, name = end_val` - // ^^^^^^^^^ between_span - TokenKind::Ident | TokenKind::Eq if seen_comma => {}, - // An unexpected token usually indicates that we crossed a macro boundary - // - // `println!(some_proc_macro!("input {}"), a)` - // ^^^ between_span - // `println!("{}", val!(x))` - // ^^^^^^^ between_span - _ => return true, - } - } - - if !seen_comma { - return true; - } - } - - false + .map(|(start, end)| start.between(end)) + .all(|sp| { + sp.check_source_text(cx, |src| { + // text should be either `, name` or `, name =` + let mut iter = tokenize(src).filter(|t| { + !matches!( + t.kind, + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace + ) + }); + iter.next().is_some_and(|t| matches!(t.kind, TokenKind::Comma)) + && iter.all(|t| matches!(t.kind, TokenKind::Ident | TokenKind::Eq)) + }) + }) } diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index d2200bcf7103b..42c8b218d14e8 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -7,7 +7,7 @@ use rustc_session::Session; use rustc_span::{sym, Span}; use std::str::FromStr; -use crate::source::snippet_opt; +use crate::source::SpanRangeExt; use crate::tokenize_with_text; /// Deprecation status of attributes known by Clippy. @@ -179,25 +179,23 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { /// Checks if the given span contains a `#[cfg(..)]` attribute pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { - let Some(snip) = snippet_opt(cx, s) else { - // Assume true. This would require either an invalid span, or one which crosses file boundaries. - return true; - }; - let mut iter = tokenize_with_text(&snip); + s.check_source_text(cx, |src| { + let mut iter = tokenize_with_text(src); - // Search for the token sequence [`#`, `[`, `cfg`] - while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) { - let mut iter = iter.by_ref().skip_while(|(t, _)| { - matches!( - t, - TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } - ) - }); - if matches!(iter.next(), Some((TokenKind::OpenBracket, _))) - && matches!(iter.next(), Some((TokenKind::Ident, "cfg"))) - { - return true; + // Search for the token sequence [`#`, `[`, `cfg`] + while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) { + let mut iter = iter.by_ref().skip_while(|(t, _)| { + matches!( + t, + TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } + ) + }); + if matches!(iter.next(), Some((TokenKind::OpenBracket, _))) + && matches!(iter.next(), Some((TokenKind::Ident, "cfg"))) + { + return true; + } } - } - false + false + }) } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index b0a668329e920..f61ef9ac1b05e 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,6 +1,6 @@ use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; -use crate::source::{snippet_opt, walk_span_to_context, SpanRange, SpanRangeExt}; +use crate::source::{walk_span_to_context, SpanRange, SpanRangeExt}; use crate::tokenize_with_text; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; @@ -588,10 +588,9 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &' // block with an empty span. ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]), // `{}` => `()` - ([], None) => match snippet_opt(cx, block.span) { - // Don't reduce if there are any tokens contained in the braces - Some(snip) - if tokenize(&snip) + ([], None) + if block.span.check_source_text(cx, |src| { + tokenize(src) .map(|t| t.kind) .filter(|t| { !matches!( @@ -599,11 +598,10 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &' TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace ) }) - .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) => - { - kind - }, - _ => &ExprKind::Tup(&[]), + .eq([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) + }) => + { + &ExprKind::Tup(&[]) }, ([], Some(expr)) => match expr.kind { // `{ return .. }` => `return ..` From 9450b4af483b4b10fe427c1436e75acc9ba68158 Mon Sep 17 00:00:00 2001 From: Jason Newcomb <jsnewcomb@pm.me> Date: Wed, 21 Aug 2024 14:04:04 -0400 Subject: [PATCH 52/54] Replace more uses of `snippet_opt`. --- clippy_lints/src/casts/cast_lossless.rs | 4 +- clippy_lints/src/casts/unnecessary_cast.rs | 8 +- clippy_lints/src/casts/zero_ptr.rs | 4 +- clippy_lints/src/functions/must_use.rs | 4 +- clippy_lints/src/functions/too_many_lines.rs | 91 +++++++++---------- clippy_lints/src/matches/manual_unwrap_or.rs | 8 +- ...se_sensitive_file_extension_comparisons.rs | 12 ++- .../src/methods/filter_map_bool_then.rs | 8 +- .../methods/from_iter_instead_of_collect.rs | 4 +- .../src/methods/join_absolute_paths.rs | 4 +- clippy_lints/src/methods/manual_ok_or.rs | 8 +- clippy_lints/src/methods/manual_try_fold.rs | 10 +- .../methods/needless_character_iteration.rs | 6 +- .../src/methods/needless_option_as_deref.rs | 4 +- .../src/methods/string_lit_chars_any.rs | 4 +- .../src/methods/unnecessary_get_then_check.rs | 10 +- .../src/methods/unnecessary_to_owned.rs | 18 ++-- .../src/methods/unused_enumerate_index.rs | 12 ++- clippy_lints/src/vec.rs | 8 +- clippy_utils/src/source.rs | 7 +- 20 files changed, 125 insertions(+), 109 deletions(-) diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index bd3acc06f4b2b..346aed7e9f117 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,7 +1,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_const_context; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; @@ -34,7 +34,7 @@ pub(super) fn check( diag.help("an `as` cast can become silently lossy if the types change in the future"); let mut applicability = Applicability::MachineApplicable; let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "<from>", &mut applicability); - let Some(ty) = snippet_opt(cx, hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt())) else { + let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_source_text(cx) else { return; }; match cast_to_hir.kind { diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index fb0b0cba6a662..566adc83d6903 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{snippet_opt, SpanRangeExt}; use clippy_utils::visitors::{for_each_expr_without_closures, Visitable}; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; @@ -104,7 +104,7 @@ pub(super) fn check<'tcx>( let literal_str = &cast_str; if let LitKind::Int(n, _) = lit.node - && let Some(src) = snippet_opt(cx, cast_expr.span) + && let Some(src) = cast_expr.span.get_source_text(cx) && cast_to.is_floating_point() && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) && let from_nbits = 128 - n.get().leading_zeros() @@ -131,7 +131,7 @@ pub(super) fn check<'tcx>( | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => { - if let Some(src) = snippet_opt(cx, cast_expr.span) { + if let Some(src) = cast_expr.span.get_source_text(cx) { if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); return true; @@ -253,7 +253,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx let res = cx.qpath_res(&qpath, expr.hir_id); // Function call if let Res::Def(DefKind::Fn, def_id) = res { - let Some(snippet) = snippet_opt(cx, cx.tcx.def_span(def_id)) else { + let Some(snippet) = cx.tcx.def_span(def_id).get_source_text(cx) else { return ControlFlow::Continue(()); }; // This is the worst part of this entire function. This is the only way I know of to diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs index 3c1c7d2dc3a54..c5c4a28646d27 100644 --- a/clippy_lints/src/casts/zero_ptr.rs +++ b/clippy_lints/src/casts/zero_ptr.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; @@ -20,7 +20,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_> let sugg = if let TyKind::Infer = mut_ty.ty.kind { format!("{std_or_core}::{sugg_fn}()") - } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { + } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) { format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") } else { return; diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index a08415aeb98a1..9382812104706 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -12,7 +12,7 @@ use rustc_span::{sym, Span}; 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::source::SpanRangeExt; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{return_ty, trait_ref_of_method}; @@ -155,7 +155,7 @@ fn check_must_use_candidate<'tcx>( return; } span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| { - if let Some(snippet) = snippet_opt(cx, fn_span) { + if let Some(snippet) = fn_span.get_source_text(cx) { diag.span_suggestion( fn_span, "add the attribute", diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index 586ca58d60dd6..0f5ce340c44dd 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -1,12 +1,11 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::source::SpanRangeExt; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_span::Span; -use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::snippet_opt; - use super::TOO_MANY_LINES; pub(super) fn check_fn( @@ -22,57 +21,57 @@ pub(super) fn check_fn( return; } - let Some(code_snippet) = snippet_opt(cx, body.value.span) else { - return; - }; let mut line_count: u64 = 0; - let mut in_comment = false; - let mut code_in_line; + let too_many = body.value.span.check_source_text(cx, |src| { + let mut in_comment = false; + let mut code_in_line; - let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..)) - && code_snippet.as_bytes().first().copied() == Some(b'{') - && code_snippet.as_bytes().last().copied() == Some(b'}') - { - // Removing the braces from the enclosing block - &code_snippet[1..code_snippet.len() - 1] - } else { - &code_snippet - } - .trim() // Remove leading and trailing blank lines - .lines(); + let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..)) + && src.as_bytes().first().copied() == Some(b'{') + && src.as_bytes().last().copied() == Some(b'}') + { + // Removing the braces from the enclosing block + &src[1..src.len() - 1] + } else { + src + } + .trim() // Remove leading and trailing blank lines + .lines(); - for mut line in function_lines { - code_in_line = false; - loop { - line = line.trim_start(); - if line.is_empty() { - break; - } - if in_comment { - if let Some(i) = line.find("*/") { - line = &line[i + 2..]; - in_comment = false; - continue; + for mut line in function_lines { + code_in_line = false; + loop { + line = line.trim_start(); + if line.is_empty() { + break; } - } else { - let multi_idx = line.find("/*").unwrap_or(line.len()); - let single_idx = line.find("//").unwrap_or(line.len()); - code_in_line |= multi_idx > 0 && single_idx > 0; - // Implies multi_idx is below line.len() - if multi_idx < single_idx { - line = &line[multi_idx + 2..]; - in_comment = true; - continue; + if in_comment { + if let Some(i) = line.find("*/") { + line = &line[i + 2..]; + in_comment = false; + continue; + } + } else { + let multi_idx = line.find("/*").unwrap_or(line.len()); + let single_idx = line.find("//").unwrap_or(line.len()); + code_in_line |= multi_idx > 0 && single_idx > 0; + // Implies multi_idx is below line.len() + if multi_idx < single_idx { + line = &line[multi_idx + 2..]; + in_comment = true; + continue; + } } + break; + } + if code_in_line { + line_count += 1; } - break; - } - if code_in_line { - line_count += 1; } - } + line_count > too_many_lines_threshold + }); - if line_count > too_many_lines_threshold { + if too_many { span_lint( cx, TOO_MANY_LINES, diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 2d4c8daf5cb66..d5d83df93471c 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; +use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg}; @@ -67,11 +67,11 @@ fn check_and_lint<'tcx>( && path_to_local_id(peel_blocks(then_expr), binding_hir_id) && cx.typeck_results().expr_adjustments(then_expr).is_empty() && let Some(ty_name) = find_type_name(cx, ty) - && let Some(or_body_snippet) = snippet_opt(cx, else_expr.span) + && let Some(or_body_snippet) = else_expr.span.get_source_text(cx) && let Some(indent) = indent_of(cx, expr.span) && ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some() { - lint(cx, expr, let_expr, ty_name, or_body_snippet, indent); + lint(cx, expr, let_expr, ty_name, &or_body_snippet, indent); } } @@ -110,7 +110,7 @@ fn lint<'tcx>( expr: &Expr<'tcx>, scrutinee: &'tcx Expr<'_>, ty_name: &str, - or_body_snippet: String, + or_body_snippet: &str, indent: usize, ) { let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent)); 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 a5df863d800bb..740cce0a6c235 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; +use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt}; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -49,10 +49,12 @@ pub(super) fn check<'tcx>( "case-sensitive file extension comparison", |diag| { diag.help("consider using a case-insensitive comparison instead"); - if let Some(mut recv_source) = snippet_opt(cx, recv.span) { - if !cx.typeck_results().expr_ty(recv).is_ref() { - recv_source = format!("&{recv_source}"); - } + if let Some(recv_source) = recv.span.get_source_text(cx) { + let recv_source = if cx.typeck_results().expr_ty(recv).is_ref() { + recv_source.to_owned() + } else { + format!("&{recv_source}") + }; let suggestion_source = reindent_multiline( format!( diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index 2e43d19a69911..4fbf661727daa 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,7 +1,7 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::BOOL_THEN; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks}; use rustc_errors::Applicability; @@ -42,9 +42,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & .iter() .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) .count() - && let Some(param_snippet) = snippet_opt(cx, param.span) - && let Some(filter) = snippet_opt(cx, recv.span) - && let Some(map) = snippet_opt(cx, then_body.span) + && let Some(param_snippet) = param.span.get_source_text(cx) + && let Some(filter) = recv.span.get_source_text(cx) + && let Some(map) = then_body.span.get_source_text(cx) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 917a8e33eb9fb..f4840785584ef 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; use clippy_utils::{is_path_diagnostic_item, sugg}; use rustc_errors::Applicability; @@ -39,7 +39,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> } let call_site = expr.span.source_callsite(); - if let Some(snippet) = snippet_opt(cx, call_site) + if let Some(snippet) = call_site.get_source_text(cx) && let snippet_split = snippet.split("::").collect::<Vec<_>>() && let Some((_, elements)) = snippet_split.split_last() { diff --git a/clippy_lints/src/methods/join_absolute_paths.rs b/clippy_lints/src/methods/join_absolute_paths.rs index aa1ec60d434a5..2dad7fcf3c129 100644 --- a/clippy_lints/src/methods/join_absolute_paths.rs +++ b/clippy_lints/src/methods/join_absolute_paths.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::expr_or_init; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -25,7 +25,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_a join_arg.span, "argument to `Path::join` starts with a path separator", |diag| { - let arg_str = snippet_opt(cx, spanned.span).unwrap_or_else(|| "..".to_string()); + let arg_str = snippet(cx, spanned.span, ".."); let no_separator = if sym_str.starts_with('/') { arg_str.replacen('/', "", 1) diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index b1af0083e65a9..b1a8e1e5e4709 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; +use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use rustc_errors::Applicability; @@ -23,11 +23,11 @@ pub(super) fn check<'tcx>( && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr) && is_ok_wrapping(cx, map_expr) - && let Some(recv_snippet) = snippet_opt(cx, recv.span) - && let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span) + && let Some(recv_snippet) = recv.span.get_source_text(cx) + && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx) && let Some(indent) = indent_of(cx, expr.span) { - let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); + let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str().into(), true, Some(indent + 4)); span_lint_and_sugg( cx, MANUAL_OK_OR, diff --git a/clippy_lints/src/methods/manual_try_fold.rs b/clippy_lints/src/methods/manual_try_fold.rs index f93edded729fc..11fba35c16ea3 100644 --- a/clippy_lints/src/methods/manual_try_fold.rs +++ b/clippy_lints/src/methods/manual_try_fold.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; use clippy_utils::{is_from_proc_macro, is_trait_method}; use rustc_errors::Applicability; @@ -31,13 +31,15 @@ pub(super) fn check<'tcx>( && let Res::Def(DefKind::Ctor(_, _), _) = cx.qpath_res(&qpath, path.hir_id) && let ExprKind::Closure(closure) = acc.kind && !is_from_proc_macro(cx, expr) - && let Some(args_snip) = closure.fn_arg_span.and_then(|fn_arg_span| snippet_opt(cx, fn_arg_span)) + && let Some(args_snip) = closure + .fn_arg_span + .and_then(|fn_arg_span| fn_arg_span.get_source_text(cx)) { let init_snip = rest .is_empty() .then_some(first.span) - .and_then(|span| snippet_opt(cx, span)) - .unwrap_or("...".to_owned()); + .and_then(|span| span.get_source_text(cx)) + .map_or_else(|| "...".to_owned(), |src| src.to_owned()); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index e3d7820771547..332da722a37dc 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -7,7 +7,7 @@ use rustc_span::Span; use super::utils::get_last_chain_binding_hir_id; use super::NEEDLESS_CHARACTER_ITERATION; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { @@ -35,7 +35,7 @@ fn handle_expr( && path_to_local_id(receiver, first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char - && let Some(snippet) = snippet_opt(cx, before_chars) + && let Some(snippet) = before_chars.get_source_text(cx) { span_lint_and_sugg( cx, @@ -79,7 +79,7 @@ fn handle_expr( && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id() && match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"]) && path_to_local_id(peels_expr_ref(arg), first_param) - && let Some(snippet) = snippet_opt(cx, before_chars) + && let Some(snippet) = before_chars.get_source_text(cx) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs index eaae8613d44d9..9f714fdd47bb3 100644 --- a/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::path_res; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; use rustc_errors::Applicability; @@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name expr.span, "derefed type is same as origin", "try", - snippet_opt(cx, recv.span).unwrap(), + recv.span.get_source_text(cx).unwrap().to_owned(), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/methods/string_lit_chars_any.rs b/clippy_lints/src/methods/string_lit_chars_any.rs index 5f6f027a3b59e..cc0d432b799d6 100644 --- a/clippy_lints/src/methods/string_lit_chars_any.rs +++ b/clippy_lints/src/methods/string_lit_chars_any.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local}; use itertools::Itertools; use rustc_ast::LitKind; @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( _ => return, } && !is_from_proc_macro(cx, expr) - && let Some(scrutinee_snip) = snippet_opt(cx, scrutinee.span) + && let Some(scrutinee_snip) = scrutinee.span.get_source_text(cx) { // Normalize the char using `map` so `join` doesn't use `Display`, if we don't then // something like `r"\"` will become `'\'`, which is of course invalid diff --git a/clippy_lints/src/methods/unnecessary_get_then_check.rs b/clippy_lints/src/methods/unnecessary_get_then_check.rs index f6184222d8e96..64eb84117954c 100644 --- a/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; @@ -38,11 +38,11 @@ pub(super) fn check( return; }; let both_calls_span = get_call_span.with_hi(call_span.hi()); - if let Some(snippet) = snippet_opt(cx, both_calls_span) - && let Some(arg_snippet) = snippet_opt(cx, arg.span) + if let Some(snippet) = both_calls_span.get_source_text(cx) + && let Some(arg_snippet) = arg.span.get_source_text(cx) { let generics_snippet = if let Some(generics) = path.args - && let Some(generics_snippet) = snippet_opt(cx, generics.span_ext) + && let Some(generics_snippet) = generics.span_ext.get_source_text(cx) { format!("::{generics_snippet}") } else { @@ -63,7 +63,7 @@ pub(super) fn check( suggestion, Applicability::MaybeIncorrect, ); - } else if let Some(caller_snippet) = snippet_opt(cx, get_caller.span) { + } else if let Some(caller_snippet) = get_caller.span.get_source_text(cx) { let full_span = get_caller.span.with_hi(call_span.hi()); span_lint_and_then( diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index fed2b128dcf30..69c5bc57e2996 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -2,7 +2,7 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::source::{snippet, SpanRangeExt}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ @@ -133,7 +133,7 @@ fn check_addr_of_expr( && (*referent_ty != receiver_ty || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty)) || is_cow_into_owned(cx, method_name, method_def_id)) - && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + && let Some(receiver_snippet) = receiver.span.get_source_text(cx) { if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { span_lint_and_sugg( @@ -167,7 +167,7 @@ fn check_addr_of_expr( parent.span, format!("unnecessary use of `{method_name}`"), "use", - receiver_snippet, + receiver_snippet.to_owned(), Applicability::MachineApplicable, ); } else { @@ -217,7 +217,7 @@ fn check_into_iter_call_arg( && let parent_ty = cx.typeck_results().expr_ty(parent) && implements_trait(cx, parent_ty, iterator_trait_id, &[]) && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) - && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + && let Some(receiver_snippet) = receiver.span.get_source_text(cx) { if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; @@ -309,8 +309,8 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb if let Some(parent) = get_parent_expr(cx, expr) && let Some((fn_name, argument_expr)) = get_fn_name_and_arg(cx, parent) && fn_name.as_str() == "split" - && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) - && let Some(arg_snippet) = snippet_opt(cx, argument_expr.span) + && let Some(receiver_snippet) = receiver.span.get_source_text(cx) + && let Some(arg_snippet) = argument_expr.span.get_source_text(cx) { // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x` // implements `AsRef<str>` but does not implement `Deref<Target = str>`. In this case, we have to @@ -405,7 +405,7 @@ fn check_other_call_arg<'tcx>( None } && can_change_type(cx, maybe_arg, receiver_ty) - && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + && let Some(receiver_snippet) = receiver.span.get_source_text(cx) { span_lint_and_sugg( cx, @@ -695,7 +695,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx && let arg_ty = arg_ty.peel_refs() // For now we limit this lint to `String` and `Vec`. && (is_str_and_string(cx, arg_ty, original_arg_ty) || is_slice_and_vec(cx, arg_ty, original_arg_ty)) - && let Some(snippet) = snippet_opt(cx, caller.span) + && let Some(snippet) = caller.span.get_source_text(cx) { span_lint_and_sugg( cx, @@ -706,7 +706,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx if original_arg_ty.is_array() { format!("{snippet}.as_slice()") } else { - snippet + snippet.to_owned() }, Applicability::MaybeIncorrect, ); diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index 3004d9c4233a7..ee5177d1ffaa1 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::source::{snippet, SpanRangeExt}; use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; @@ -81,8 +81,14 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) { // We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`. // Fallback to `..` if we fail getting either snippet. - Some(ty_span) => snippet_opt(cx, elem.span) - .and_then(|binding_name| snippet_opt(cx, ty_span).map(|ty_name| format!("{binding_name}: {ty_name}"))) + Some(ty_span) => elem + .span + .get_source_text(cx) + .and_then(|binding_name| { + ty_span + .get_source_text(cx) + .map(|ty_name| format!("{binding_name}: {ty_name}")) + }) .unwrap_or_else(|| "..".to_string()), // Otherwise, we have no explicit type. We can replace with the binding name of the element. None => snippet(cx, elem.span, "..").into_owned(), diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 228db14d1b7c3..d3e49bff422ca 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -5,7 +5,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method}; @@ -214,9 +214,11 @@ impl SuggestedType { } fn snippet(self, cx: &LateContext<'_>, args_span: Option<Span>, len_span: Option<Span>) -> String { - let maybe_args = args_span.and_then(|sp| snippet_opt(cx, sp)).unwrap_or_default(); + let maybe_args = args_span + .and_then(|sp| sp.get_source_text(cx)) + .map_or(String::new(), |x| x.to_owned()); let maybe_len = len_span - .and_then(|sp| snippet_opt(cx, sp).map(|s| format!("; {s}"))) + .and_then(|sp| sp.get_source_text(cx).map(|s| format!("; {s}"))) .unwrap_or_default(); match self { diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 8b49b013e0313..d6830b53b1571 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -207,6 +207,7 @@ fn get_source_range(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRang if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos { return None; } + sm.ensure_source_file_source_present(&start.sf); let range = start.pos.to_usize()..end.pos.to_usize(); Some(SourceFileRange { sf: start.sf, range }) } @@ -283,7 +284,11 @@ impl SourceFileRange { /// Attempts to get the text from the source file. This can fail if the source text isn't /// loaded. pub fn as_str(&self) -> Option<&str> { - self.sf.src.as_ref().and_then(|x| x.get(self.range.clone())) + self.sf + .src + .as_ref() + .or_else(|| self.sf.external_src.get().and_then(|src| src.get_source())) + .and_then(|x| x.get(self.range.clone())) } } From f4fc3858bc58d2e51c545ba5b3cda592225e51fc Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk <alexsemenyuk88@gmail.com> Date: Sat, 24 Aug 2024 15:43:00 +0500 Subject: [PATCH 53/54] Fix suggestion unwrap_or_else --- .../src/methods/unnecessary_lazy_eval.rs | 4 +- tests/ui/unnecessary_lazy_eval.stderr | 566 ++++++++++++------ .../ui/unnecessary_lazy_eval_unfixable.stderr | 35 +- 3 files changed, 402 insertions(+), 203 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 4429f0326058a..b84594c0da19f 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -64,9 +64,9 @@ pub(super) fn check<'tcx>( // but prefer to avoid changing the signature of the function itself. if let hir::ExprKind::MethodCall(.., span) = expr.kind { span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { - diag.span_suggestion( + diag.span_suggestion_verbose( span, - format!("use `{simplify_using}(..)` instead"), + format!("use `{simplify_using}` instead"), format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), applicability, ); diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index fcd60f48bccdd..bcdf65b217e51 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -2,316 +2,432 @@ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:83:13 | LL | let _ = opt.unwrap_or_else(|| 2); - | ^^^^-------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_lazy_evaluations)]` +help: use `unwrap_or` instead + | +LL | let _ = opt.unwrap_or(2); + | ~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:84:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); - | ^^^^--------------------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = opt.unwrap_or(astronomers_pi); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:85:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); - | ^^^^------------------------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = opt.unwrap_or(ext_str.some_field); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:87:13 | LL | let _ = opt.and_then(|_| ext_opt); - | ^^^^--------------------- - | | - | help: use `and(..)` instead: `and(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `and` instead + | +LL | let _ = opt.and(ext_opt); + | ~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:88:13 | LL | let _ = opt.or_else(|| ext_opt); - | ^^^^------------------- - | | - | help: use `or(..)` instead: `or(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `or` instead + | +LL | let _ = opt.or(ext_opt); + | ~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:89:13 | LL | let _ = opt.or_else(|| None); - | ^^^^---------------- - | | - | help: use `or(..)` instead: `or(None)` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `or` instead + | +LL | let _ = opt.or(None); + | ~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:90:13 | LL | let _ = opt.get_or_insert_with(|| 2); - | ^^^^------------------------ - | | - | help: use `get_or_insert(..)` instead: `get_or_insert(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `get_or_insert` instead + | +LL | let _ = opt.get_or_insert(2); + | ~~~~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:91:13 | LL | let _ = opt.ok_or_else(|| 2); - | ^^^^---------------- - | | - | help: use `ok_or(..)` instead: `ok_or(2)` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `ok_or` instead + | +LL | let _ = opt.ok_or(2); + | ~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:92:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); - | ^^^^^^^^^^^^^^^^^------------------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = nested_tuple_opt.unwrap_or(Some((1, 2))); + | ~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:93:13 | LL | let _ = cond.then(|| astronomers_pi); - | ^^^^^----------------------- - | | - | help: use `then_some(..)` instead: `then_some(astronomers_pi)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _ = cond.then_some(astronomers_pi); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:94:13 | LL | let _ = true.then(|| -> _ {}); - | ^^^^^---------------- - | | - | help: use `then_some(..)` instead: `then_some({})` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _ = true.then_some({}); + | ~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:95:13 | LL | let _ = true.then(|| {}); - | ^^^^^----------- - | | - | help: use `then_some(..)` instead: `then_some({})` + | ^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _ = true.then_some({}); + | ~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:99:13 | LL | let _ = Some(1).unwrap_or_else(|| *r); - | ^^^^^^^^--------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(*r)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = Some(1).unwrap_or(*r); + | ~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:101:13 | LL | let _ = Some(1).unwrap_or_else(|| *b); - | ^^^^^^^^--------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(*b)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = Some(1).unwrap_or(*b); + | ~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:103:13 | LL | let _ = Some(1).as_ref().unwrap_or_else(|| &r); - | ^^^^^^^^^^^^^^^^^--------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(&r)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = Some(1).as_ref().unwrap_or(&r); + | ~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:104:13 | LL | let _ = Some(1).as_ref().unwrap_or_else(|| &b); - | ^^^^^^^^^^^^^^^^^--------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(&b)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = Some(1).as_ref().unwrap_or(&b); + | ~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:107:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); - | ^^^^^^^^^-------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = Some(10).unwrap_or(2); + | ~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:108:13 | LL | let _ = Some(10).and_then(|_| ext_opt); - | ^^^^^^^^^--------------------- - | | - | help: use `and(..)` instead: `and(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `and` instead + | +LL | let _ = Some(10).and(ext_opt); + | ~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:109:28 | LL | let _: Option<usize> = None.or_else(|| ext_opt); - | ^^^^^------------------- - | | - | help: use `or(..)` instead: `or(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `or` instead + | +LL | let _: Option<usize> = None.or(ext_opt); + | ~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:110:13 | LL | let _ = None.get_or_insert_with(|| 2); - | ^^^^^------------------------ - | | - | help: use `get_or_insert(..)` instead: `get_or_insert(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `get_or_insert` instead + | +LL | let _ = None.get_or_insert(2); + | ~~~~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:111:35 | LL | let _: Result<usize, usize> = None.ok_or_else(|| 2); - | ^^^^^---------------- - | | - | help: use `ok_or(..)` instead: `ok_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `ok_or` instead + | +LL | let _: Result<usize, usize> = None.ok_or(2); + | ~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:112:28 | LL | let _: Option<usize> = None.or_else(|| None); - | ^^^^^---------------- - | | - | help: use `or(..)` instead: `or(None)` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `or` instead + | +LL | let _: Option<usize> = None.or(None); + | ~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:115:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); - | ^^^^^^^-------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = deep.0.unwrap_or(2); + | ~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:116:13 | LL | let _ = deep.0.and_then(|_| ext_opt); - | ^^^^^^^--------------------- - | | - | help: use `and(..)` instead: `and(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `and` instead + | +LL | let _ = deep.0.and(ext_opt); + | ~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:117:13 | LL | let _ = deep.0.or_else(|| None); - | ^^^^^^^---------------- - | | - | help: use `or(..)` instead: `or(None)` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `or` instead + | +LL | let _ = deep.0.or(None); + | ~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:118:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); - | ^^^^^^^------------------------ - | | - | help: use `get_or_insert(..)` instead: `get_or_insert(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `get_or_insert` instead + | +LL | let _ = deep.0.get_or_insert(2); + | ~~~~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:119:13 | LL | let _ = deep.0.ok_or_else(|| 2); - | ^^^^^^^---------------- - | | - | help: use `ok_or(..)` instead: `ok_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `ok_or` instead + | +LL | let _ = deep.0.ok_or(2); + | ~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:150:28 | LL | let _: Option<usize> = None.or_else(|| Some(3)); - | ^^^^^------------------- - | | - | help: use `or(..)` instead: `or(Some(3))` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `or` instead + | +LL | let _: Option<usize> = None.or(Some(3)); + | ~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:151:13 | LL | let _ = deep.0.or_else(|| Some(3)); - | ^^^^^^^------------------- - | | - | help: use `or(..)` instead: `or(Some(3))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `or` instead + | +LL | let _ = deep.0.or(Some(3)); + | ~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Option::None` --> tests/ui/unnecessary_lazy_eval.rs:152:13 | LL | let _ = opt.or_else(|| Some(3)); - | ^^^^------------------- - | | - | help: use `or(..)` instead: `or(Some(3))` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `or` instead + | +LL | let _ = opt.or(Some(3)); + | ~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval.rs:158:13 | LL | let _ = res2.unwrap_or_else(|_| 2); - | ^^^^^--------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = res2.unwrap_or(2); + | ~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval.rs:159:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); - | ^^^^^---------------------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = res2.unwrap_or(astronomers_pi); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval.rs:160:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); - | ^^^^^-------------------------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = res2.unwrap_or(ext_str.some_field); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval.rs:182:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(2)); - | ^^^^-------------------- - | | - | help: use `and(..)` instead: `and(Err(2))` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `and` instead + | +LL | let _: Result<usize, usize> = res.and(Err(2)); + | ~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval.rs:183:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi)); - | ^^^^--------------------------------- - | | - | help: use `and(..)` instead: `and(Err(astronomers_pi))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `and` instead + | +LL | let _: Result<usize, usize> = res.and(Err(astronomers_pi)); + | ~~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval.rs:184:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field)); - | ^^^^------------------------------------- - | | - | help: use `and(..)` instead: `and(Err(ext_str.some_field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `and` instead + | +LL | let _: Result<usize, usize> = res.and(Err(ext_str.some_field)); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval.rs:186:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2)); - | ^^^^------------------ - | | - | help: use `or(..)` instead: `or(Ok(2))` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `or` instead + | +LL | let _: Result<usize, usize> = res.or(Ok(2)); + | ~~~~~~~~~ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval.rs:187:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi)); - | ^^^^------------------------------- - | | - | help: use `or(..)` instead: `or(Ok(astronomers_pi))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `or` instead + | +LL | let _: Result<usize, usize> = res.or(Ok(astronomers_pi)); + | ~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval.rs:188:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field)); - | ^^^^----------------------------------- - | | - | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `or` instead + | +LL | let _: Result<usize, usize> = res.or(Ok(ext_str.some_field)); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval.rs:189:35 @@ -324,193 +440,265 @@ LL | | // some lines ... | LL | | // some lines LL | | or_else(|_| Ok(ext_str.some_field)); - | |_____----------------------------------^ - | | - | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` + | |_______________________________________^ + | +help: use `or` instead + | +LL | or(Ok(ext_str.some_field)); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:219:14 | LL | let _x = false.then(|| i32::MAX + 1); - | ^^^^^^--------------------- - | | - | help: use `then_some(..)` instead: `then_some(i32::MAX + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(i32::MAX + 1); + | ~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:221:14 | LL | let _x = false.then(|| i32::MAX * 2); - | ^^^^^^--------------------- - | | - | help: use `then_some(..)` instead: `then_some(i32::MAX * 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(i32::MAX * 2); + | ~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:223:14 | LL | let _x = false.then(|| i32::MAX - 1); - | ^^^^^^--------------------- - | | - | help: use `then_some(..)` instead: `then_some(i32::MAX - 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(i32::MAX - 1); + | ~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:225:14 | LL | let _x = false.then(|| i32::MIN - 1); - | ^^^^^^--------------------- - | | - | help: use `then_some(..)` instead: `then_some(i32::MIN - 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(i32::MIN - 1); + | ~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:227:14 | LL | let _x = false.then(|| (1 + 2 * 3 - 2 / 3 + 9) << 2); - | ^^^^^^------------------------------------- - | | - | help: use `then_some(..)` instead: `then_some((1 + 2 * 3 - 2 / 3 + 9) << 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some((1 + 2 * 3 - 2 / 3 + 9) << 2); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:229:14 | LL | let _x = false.then(|| 255u8 << 7); - | ^^^^^^------------------- - | | - | help: use `then_some(..)` instead: `then_some(255u8 << 7)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(255u8 << 7); + | ~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:231:14 | LL | let _x = false.then(|| 255u8 << 8); - | ^^^^^^------------------- - | | - | help: use `then_some(..)` instead: `then_some(255u8 << 8)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(255u8 << 8); + | ~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:233:14 | LL | let _x = false.then(|| 255u8 >> 8); - | ^^^^^^------------------- - | | - | help: use `then_some(..)` instead: `then_some(255u8 >> 8)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(255u8 >> 8); + | ~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:236:14 | LL | let _x = false.then(|| i32::MAX + -1); - | ^^^^^^---------------------- - | | - | help: use `then_some(..)` instead: `then_some(i32::MAX + -1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(i32::MAX + -1); + | ~~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:238:14 | LL | let _x = false.then(|| -i32::MAX); - | ^^^^^^------------------ - | | - | help: use `then_some(..)` instead: `then_some(-i32::MAX)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(-i32::MAX); + | ~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:240:14 | LL | let _x = false.then(|| -i32::MIN); - | ^^^^^^------------------ - | | - | help: use `then_some(..)` instead: `then_some(-i32::MIN)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(-i32::MIN); + | ~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:243:14 | LL | let _x = false.then(|| 255 >> -7); - | ^^^^^^------------------ - | | - | help: use `then_some(..)` instead: `then_some(255 >> -7)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(255 >> -7); + | ~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:245:14 | LL | let _x = false.then(|| 255 << -1); - | ^^^^^^------------------ - | | - | help: use `then_some(..)` instead: `then_some(255 << -1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(255 << -1); + | ~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:247:14 | LL | let _x = false.then(|| 1 / 0); - | ^^^^^^-------------- - | | - | help: use `then_some(..)` instead: `then_some(1 / 0)` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(1 / 0); + | ~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:249:14 | LL | let _x = false.then(|| x << -1); - | ^^^^^^---------------- - | | - | help: use `then_some(..)` instead: `then_some(x << -1)` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(x << -1); + | ~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:251:14 | LL | let _x = false.then(|| x << 2); - | ^^^^^^--------------- - | | - | help: use `then_some(..)` instead: `then_some(x << 2)` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(x << 2); + | ~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:261:14 | LL | let _x = false.then(|| x / 0); - | ^^^^^^-------------- - | | - | help: use `then_some(..)` instead: `then_some(x / 0)` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(x / 0); + | ~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:263:14 | LL | let _x = false.then(|| x % 0); - | ^^^^^^-------------- - | | - | help: use `then_some(..)` instead: `then_some(x % 0)` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(x % 0); + | ~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:266:14 | LL | let _x = false.then(|| 1 / -1); - | ^^^^^^--------------- - | | - | help: use `then_some(..)` instead: `then_some(1 / -1)` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(1 / -1); + | ~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:268:14 | LL | let _x = false.then(|| i32::MIN / -1); - | ^^^^^^---------------------- - | | - | help: use `then_some(..)` instead: `then_some(i32::MIN / -1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(i32::MIN / -1); + | ~~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:271:14 | LL | let _x = false.then(|| i32::MIN / 0); - | ^^^^^^--------------------- - | | - | help: use `then_some(..)` instead: `then_some(i32::MIN / 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(i32::MIN / 0); + | ~~~~~~~~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:273:14 | LL | let _x = false.then(|| 4 / 2); - | ^^^^^^-------------- - | | - | help: use `then_some(..)` instead: `then_some(4 / 2)` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(4 / 2); + | ~~~~~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval.rs:281:14 | LL | let _x = false.then(|| f1 + f2); - | ^^^^^^---------------- - | | - | help: use `then_some(..)` instead: `then_some(f1 + f2)` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _x = false.then_some(f1 + f2); + | ~~~~~~~~~~~~~~~~~~ error: aborting due to 63 previous errors diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/tests/ui/unnecessary_lazy_eval_unfixable.stderr index b566b119571cb..390235b212474 100644 --- a/tests/ui/unnecessary_lazy_eval_unfixable.stderr +++ b/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -2,36 +2,47 @@ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval_unfixable.rs:13:13 | LL | let _ = Ok(1).unwrap_or_else(|()| 2); - | ^^^^^^---------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_lazy_evaluations)]` +help: use `unwrap_or` instead + | +LL | let _ = Ok(1).unwrap_or(2); + | ~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval_unfixable.rs:19:13 | LL | let _ = Ok(1).unwrap_or_else(|e::E| 2); - | ^^^^^^------------------------ - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = Ok(1).unwrap_or(2); + | ~~~~~~~~~~~~ error: unnecessary closure used to substitute value for `Result::Err` --> tests/ui/unnecessary_lazy_eval_unfixable.rs:21:13 | LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); - | ^^^^^^------------------------------------- - | | - | help: use `unwrap_or(..)` instead: `unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL | let _ = Ok(1).unwrap_or(2); + | ~~~~~~~~~~~~ error: unnecessary closure used with `bool::then` --> tests/ui/unnecessary_lazy_eval_unfixable.rs:31:13 | LL | let _ = true.then(|| -> &[u8] { &[] }); - | ^^^^^------------------------- - | | - | help: use `then_some(..)` instead: `then_some({ &[] })` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL | let _ = true.then_some({ &[] }); + | ~~~~~~~~~~~~~~~~~~ error: aborting due to 4 previous errors From 0196e2d1dc50e43c6ae6132690e30d355ca56d67 Mon Sep 17 00:00:00 2001 From: Jason Newcomb <jsnewcomb@pm.me> Date: Sat, 24 Aug 2024 16:31:55 -0400 Subject: [PATCH 54/54] Bump nightly version -> 2024-08-23 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 5fbe4e544a9e4..0be2e81810ebe 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-08-08" +channel = "nightly-2024-08-23" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal"