diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 94f494b65c4..32dc251c836 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -52,7 +52,7 @@ jobs: run: cargo generate-lockfile - name: Cache - uses: Swatinem/rust-cache@v2.7.0 + uses: Swatinem/rust-cache@v2 with: save-if: ${{ github.ref == 'refs/heads/master' }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bdbc91db93..161fa630ed4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5331,6 +5331,7 @@ Released 2018-09-13 [`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant +[`arbitrary_source_item_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering [`arc_with_non_send_sync`]: https://rust-lang.github.io/rust-clippy/master/index.html#arc_with_non_send_sync [`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions @@ -5689,6 +5690,7 @@ Released 2018-09-13 [`manual_unwrap_or_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default [`manual_while_let_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_while_let_some [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names +[`map_all_any_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_all_any_identity [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry @@ -5696,6 +5698,7 @@ Released 2018-09-13 [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten [`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity [`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or +[`map_with_unused_argument_over_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_with_unused_argument_over_ranges [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool [`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro @@ -5761,6 +5764,7 @@ Released 2018-09-13 [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer [`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount [`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type +[`needless_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_as_bytes [`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool [`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign @@ -6205,12 +6209,14 @@ Released 2018-09-13 [`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds [`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold [`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items +[`module-item-order-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-item-order-groupings [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline [`semicolon-outside-block-ignore-multiline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-outside-block-ignore-multiline [`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold +[`source-item-ordering`]: https://doc.rust-lang.org/clippy/lint_configuration.html#source-item-ordering [`stack-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#stack-size-threshold [`standard-macro-braces`]: https://doc.rust-lang.org/clippy/lint_configuration.html#standard-macro-braces [`struct-field-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#struct-field-name-threshold @@ -6218,6 +6224,7 @@ Released 2018-09-13 [`too-large-for-stack`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-large-for-stack [`too-many-arguments-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-arguments-threshold [`too-many-lines-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-lines-threshold +[`trait-assoc-item-kinds-order`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trait-assoc-item-kinds-order [`trivial-copy-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trivial-copy-size-limit [`type-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#type-complexity-threshold [`unnecessary-box-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unnecessary-box-size diff --git a/Cargo.toml b/Cargo.toml index 1f7784fc489..ee9c57ab835 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ toml = "0.7.3" walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" -pulldown-cmark = "0.11" +pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } rinja = { version = "0.3", default-features = false, features = ["config"] } # UI test dependencies @@ -69,3 +69,10 @@ harness = false [[test]] name = "dogfood" harness = false + +# quine-mc_cluskey makes up a significant part of the runtime in dogfood +# due to the number of conditions in the clippy_lints crate +# and enabling optimizations for that specific dependency helps a bit +# without increasing total build times. +[profile.dev.package.quine-mc_cluskey] +opt-level = 3 diff --git a/README.md b/README.md index ec76a6dfb08..1690e2beb16 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/book/src/README.md b/book/src/README.md index 7bdfb97c3ac..23527ba896a 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index 09171d86a20..77910917963 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for MyStructLint { // Check our expr is calling a method if let hir::ExprKind::MethodCall(path, _, _self_arg, ..) = &expr.kind // Check the name of this method is `some_method` - && path.ident.name == sym!(some_method) + && path.ident.name.as_str() == "some_method" // Optionally, check the type of the self argument. // - See "Checking for a specific type" { @@ -167,7 +167,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { // Check if item is a method/function if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // Check the method is named `some_method` - && impl_item.ident.name == sym!(some_method) + && impl_item.ident.name.as_str() == "some_method" // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go further and even check if its return type is `String` diff --git a/book/src/development/method_checking.md b/book/src/development/method_checking.md index 56d1be37519..9c5d4b516db 100644 --- a/book/src/development/method_checking.md +++ b/book/src/development/method_checking.md @@ -3,8 +3,8 @@ In some scenarios we might want to check for methods when developing a lint. There are two kinds of questions that we might be curious about: -- Invocation: Does an expression call a specific method? -- Definition: Does an `impl` define a method? +- Invocation: Does an expression call a specific method? +- Definition: Does an `impl` define a method? ## Checking if an `expr` is calling a specific method @@ -23,7 +23,7 @@ impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { // Check our expr is calling a method with pattern matching if let hir::ExprKind::MethodCall(path, _, [self_arg, ..]) = &expr.kind // Check if the name of this method is `our_fancy_method` - && path.ident.name == sym!(our_fancy_method) + && path.ident.name.as_str() == "our_fancy_method" // We can check the type of the self argument whenever necessary. // (It's necessary if we want to check that method is specifically belonging to a specific trait, // for example, a `map` method could belong to user-defined trait instead of to `Iterator`) @@ -41,10 +41,6 @@ information on the pattern matching. As mentioned in [Define Lints](defining_lints.md#lint-types), the `methods` lint type is full of pattern matching with `MethodCall` in case the reader wishes to explore more. -Additionally, we use the [`clippy_utils::sym!`][sym] macro to conveniently -convert an input `our_fancy_method` into a `Symbol` and compare that symbol to -the [`Ident`]'s name in the [`PathSegment`] in the [`MethodCall`]. - ## Checking if a `impl` block implements a method While sometimes we want to check whether a method is being called or not, other @@ -71,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { // Check if item is a method/function if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // Check the method is named `our_fancy_method` - && impl_item.ident.name == sym!(our_fancy_method) + && impl_item.ident.name.as_str() == "our_fancy_method" // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go even further and even check if its return type is `String` @@ -85,9 +81,6 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { [`check_impl_item`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_impl_item [`ExprKind`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/hir/enum.ExprKind.html -[`Ident`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_span/symbol/struct.Ident.html [`ImplItem`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_hir/hir/struct.ImplItem.html [`LateLintPass`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_lint/trait.LateLintPass.html [`MethodCall`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/hir/enum.ExprKind.html#variant.MethodCall -[`PathSegment`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/hir/struct.PathSegment.html -[sym]: https://doc.rust-lang.org/stable/nightly-rustc/clippy_utils/macro.sym.html diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 43b551ae216..670b5cbef82 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -456,7 +456,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:** `["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"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "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:** @@ -666,6 +666,16 @@ crate. For example, `pub(crate)` items. * [`missing_docs_in_private_items`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items) +## `module-item-order-groupings` +The named groupings of different source item kinds within modules. + +**Default Value:** `[["modules", ["extern_crate", "mod", "foreign_mod"]], ["use", ["use"]], ["macros", ["macro"]], ["global_asm", ["global_asm"]], ["UPPER_SNAKE_CASE", ["static", "const"]], ["PascalCase", ["ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl"]], ["lower_snake_case", ["fn"]]]` + +--- +**Affected lints:** +* [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering) + + ## `msrv` The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` @@ -710,6 +720,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold) * [`map_clone`](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) * [`map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or) +* [`map_with_unused_argument_over_ranges`](https://rust-lang.github.io/rust-clippy/master/index.html#map_with_unused_argument_over_ranges) * [`match_like_matches_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro) * [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default) * [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn) @@ -783,6 +794,16 @@ The maximum number of single char bindings a scope may have * [`many_single_char_names`](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names) +## `source-item-ordering` +Which kind of elements should be ordered internally, possible values being `enum`, `impl`, `module`, `struct`, `trait`. + +**Default Value:** `["enum", "impl", "module", "struct", "trait"]` + +--- +**Affected lints:** +* [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering) + + ## `stack-size-threshold` The maximum allowed stack size for functions in bytes @@ -862,6 +883,16 @@ The maximum number of lines a function or method can have * [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) +## `trait-assoc-item-kinds-order` +The order of associated items in traits. + +**Default Value:** `["const", "type", "fn"]` + +--- +**Affected lints:** +* [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering) + + ## `trivial-copy-size-limit` The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. By default there is no limit diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 4757c0b1339..600d5b6e2c8 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -1,6 +1,10 @@ use crate::ClippyConfiguration; use crate::msrvs::Msrv; -use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename}; +use crate::types::{ + DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, + SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, + SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, +}; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; @@ -17,8 +21,9 @@ #[rustfmt::skip] const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", + "MHz", "GHz", "THz", "AccessKit", - "CoreFoundation", "CoreGraphics", "CoreText", + "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", @@ -46,6 +51,29 @@ const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"]; const DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] = &["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"]; +const DEFAULT_MODULE_ITEM_ORDERING_GROUPS: &[(&str, &[SourceItemOrderingModuleItemKind])] = { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingModuleItemKind::*; + &[ + ("modules", &[ExternCrate, Mod, ForeignMod]), + ("use", &[Use]), + ("macros", &[Macro]), + ("global_asm", &[GlobalAsm]), + ("UPPER_SNAKE_CASE", &[Static, Const]), + ("PascalCase", &[TyAlias, Enum, Struct, Union, Trait, TraitAlias, Impl]), + ("lower_snake_case", &[Fn]), + ] +}; +const DEFAULT_TRAIT_ASSOC_ITEM_KINDS_ORDER: &[SourceItemOrderingTraitAssocItemKind] = { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingTraitAssocItemKind::*; + &[Const, Type, Fn] +}; +const DEFAULT_SOURCE_ITEM_ORDERING: &[SourceItemOrderingCategory] = { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingCategory::*; + &[Enum, Impl, Module, Struct, Trait] +}; /// Conf with parse errors #[derive(Default)] @@ -102,7 +130,9 @@ pub fn sanitize_explanation(raw_docs: &str) -> String { // Remove tags and hidden code: let mut explanation = String::with_capacity(128); let mut in_code = false; - for line in raw_docs.lines().map(str::trim) { + for line in raw_docs.lines() { + let line = line.strip_prefix(' ').unwrap_or(line); + 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") { @@ -530,6 +560,9 @@ pub fn get_configuration_metadata() -> Vec { /// crate. For example, `pub(crate)` items. #[lints(missing_docs_in_private_items)] missing_docs_in_crate_items: bool = false, + /// The named groupings of different source item kinds within modules. + #[lints(arbitrary_source_item_ordering)] + module_item_order_groupings: SourceItemOrderingModuleItemGroupings = DEFAULT_MODULE_ITEM_ORDERING_GROUPS.into(), /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` #[default_text = "current version"] #[lints( @@ -570,6 +603,7 @@ pub fn get_configuration_metadata() -> Vec { manual_try_fold, map_clone, map_unwrap_or, + map_with_unused_argument_over_ranges, match_like_matches_macro, mem_replace_with_default, missing_const_for_fn, @@ -608,6 +642,9 @@ pub fn get_configuration_metadata() -> Vec { /// The maximum number of single char bindings a scope may have #[lints(many_single_char_names)] single_char_binding_names_threshold: u64 = 4, + /// Which kind of elements should be ordered internally, possible values being `enum`, `impl`, `module`, `struct`, `trait`. + #[lints(arbitrary_source_item_ordering)] + source_item_ordering: SourceItemOrdering = DEFAULT_SOURCE_ITEM_ORDERING.into(), /// The maximum allowed stack size for functions in bytes #[lints(large_stack_frames)] stack_size_threshold: u64 = 512_000, @@ -637,6 +674,9 @@ pub fn get_configuration_metadata() -> Vec { /// The maximum number of lines a function or method can have #[lints(too_many_lines)] too_many_lines_threshold: u64 = 100, + /// The order of associated items in traits. + #[lints(arbitrary_source_item_ordering)] + trait_assoc_item_kinds_order: SourceItemOrderingTraitAssocItemKinds = DEFAULT_TRAIT_ASSOC_ITEM_KINDS_ORDER.into(), /// 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"] diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index 42651521f8d..1c3f32c2514 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -20,6 +20,7 @@ extern crate rustc_errors; extern crate rustc_session; extern crate rustc_span; +extern crate smallvec; mod conf; mod metadata; diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index 2f4da4cba3d..764ca8fb50a 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -3,6 +3,7 @@ use rustc_session::{RustcVersion, Session}; use rustc_span::{Symbol, sym}; use serde::Deserialize; +use smallvec::{SmallVec, smallvec}; use std::fmt; macro_rules! msrv_aliases { @@ -18,7 +19,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY } - 1,82,0 { IS_NONE_OR } + 1,82,0 { IS_NONE_OR, REPEAT_N } 1,81,0 { LINT_REASONS_STABILIZATION } 1,80,0 { BOX_INTO_ITER} 1,77,0 { C_STR_LITERALS } @@ -54,7 +55,7 @@ macro_rules! msrv_aliases { 1,33,0 { UNDERSCORE_IMPORTS } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } - 1,28,0 { FROM_BOOL } + 1,28,0 { FROM_BOOL, REPEAT_WITH } 1,27,0 { ITERATOR_TRY_FOLD } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } 1,24,0 { IS_ASCII_DIGIT } @@ -67,7 +68,7 @@ macro_rules! msrv_aliases { /// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]` #[derive(Debug, Clone)] pub struct Msrv { - stack: Vec, + stack: SmallVec<[RustcVersion; 2]>, } impl fmt::Display for Msrv { @@ -87,14 +88,14 @@ fn deserialize(deserializer: D) -> Result { let v = String::deserialize(deserializer)?; parse_version(Symbol::intern(&v)) - .map(|v| Msrv { stack: vec![v] }) + .map(|v| Msrv { stack: smallvec![v] }) .ok_or_else(|| serde::de::Error::custom("not a valid Rust version")) } } impl Msrv { pub fn empty() -> Msrv { - Msrv { stack: Vec::new() } + Msrv { stack: SmallVec::new() } } pub fn read_cargo(&mut self, sess: &Session) { @@ -103,7 +104,7 @@ pub fn read_cargo(&mut self, sess: &Session) { .and_then(|v| parse_version(Symbol::intern(&v))); match (self.current(), cargo_msrv) { - (None, Some(cargo_msrv)) => self.stack = vec![cargo_msrv], + (None, Some(cargo_msrv)) => self.stack = smallvec![cargo_msrv], (Some(clippy_msrv), Some(cargo_msrv)) => { if clippy_msrv != cargo_msrv { sess.dcx().warn(format!( diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index bab63911182..fe576424148 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -1,5 +1,6 @@ use serde::de::{self, Deserializer, Visitor}; use serde::{Deserialize, Serialize, ser}; +use std::collections::HashMap; use std::fmt; #[derive(Debug, Deserialize)] @@ -102,6 +103,306 @@ fn visit_map(self, mut map: V) -> Result } } +/// Represents the item categories that can be ordered by the source ordering lint. +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SourceItemOrderingCategory { + Enum, + Impl, + Module, + Struct, + Trait, +} + +/// Represents which item categories are enabled for ordering. +/// +/// The [`Deserialize`] implementation checks that there are no duplicates in +/// the user configuration. +pub struct SourceItemOrdering(Vec); + +impl SourceItemOrdering { + pub fn contains(&self, category: &SourceItemOrderingCategory) -> bool { + self.0.contains(category) + } +} + +impl From for SourceItemOrdering +where + T: Into>, +{ + fn from(value: T) -> Self { + Self(value.into()) + } +} + +impl core::fmt::Debug for SourceItemOrdering { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl<'de> Deserialize<'de> for SourceItemOrdering { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let items = Vec::::deserialize(deserializer)?; + let mut items_set = std::collections::HashSet::new(); + + for item in &items { + if items_set.contains(item) { + return Err(de::Error::custom(format!( + "The category \"{item:?}\" was enabled more than once in the source ordering configuration." + ))); + } + items_set.insert(item); + } + + Ok(Self(items)) + } +} + +impl Serialize for SourceItemOrdering { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + self.0.serialize(serializer) + } +} + +/// Represents the items that can occur within a module. +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SourceItemOrderingModuleItemKind { + ExternCrate, + Mod, + ForeignMod, + Use, + Macro, + GlobalAsm, + Static, + Const, + TyAlias, + Enum, + Struct, + Union, + Trait, + TraitAlias, + Impl, + Fn, +} + +impl SourceItemOrderingModuleItemKind { + pub fn all_variants() -> Vec { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingModuleItemKind::*; + vec![ + ExternCrate, + Mod, + ForeignMod, + Use, + Macro, + GlobalAsm, + Static, + Const, + TyAlias, + Enum, + Struct, + Union, + Trait, + TraitAlias, + Impl, + Fn, + ] + } +} + +/// Represents the configured ordering of items within a module. +/// +/// The [`Deserialize`] implementation checks that no item kinds have been +/// omitted and that there are no duplicates in the user configuration. +#[derive(Clone)] +pub struct SourceItemOrderingModuleItemGroupings { + groups: Vec<(String, Vec)>, + lut: HashMap, +} + +impl SourceItemOrderingModuleItemGroupings { + fn build_lut( + groups: &[(String, Vec)], + ) -> HashMap { + let mut lut = HashMap::new(); + for (group_index, (_, items)) in groups.iter().enumerate() { + for item in items { + lut.insert(item.clone(), group_index); + } + } + lut + } + + pub fn module_level_order_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option { + self.lut.get(item).copied() + } +} + +impl From<&[(&str, &[SourceItemOrderingModuleItemKind])]> for SourceItemOrderingModuleItemGroupings { + fn from(value: &[(&str, &[SourceItemOrderingModuleItemKind])]) -> Self { + let groups: Vec<(String, Vec)> = + value.iter().map(|item| (item.0.to_string(), item.1.to_vec())).collect(); + let lut = Self::build_lut(&groups); + Self { groups, lut } + } +} + +impl core::fmt::Debug for SourceItemOrderingModuleItemGroupings { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.groups.fmt(f) + } +} + +impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let groups = Vec::<(String, Vec)>::deserialize(deserializer)?; + let items_total: usize = groups.iter().map(|(_, v)| v.len()).sum(); + let lut = Self::build_lut(&groups); + + let mut expected_items = SourceItemOrderingModuleItemKind::all_variants(); + for item in lut.keys() { + expected_items.retain(|i| i != item); + } + + let all_items = SourceItemOrderingModuleItemKind::all_variants(); + if expected_items.is_empty() && items_total == all_items.len() { + let Some(use_group_index) = lut.get(&SourceItemOrderingModuleItemKind::Use) else { + return Err(de::Error::custom("Error in internal LUT.")); + }; + let Some((_, use_group_items)) = groups.get(*use_group_index) else { + return Err(de::Error::custom("Error in internal LUT.")); + }; + if use_group_items.len() > 1 { + return Err(de::Error::custom( + "The group containing the \"use\" item kind may not contain any other item kinds. \ + The \"use\" items will (generally) be sorted by rustfmt already. \ + Therefore it makes no sense to implement linting rules that may conflict with rustfmt.", + )); + } + + Ok(Self { groups, lut }) + } else if items_total != all_items.len() { + Err(de::Error::custom(format!( + "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \ + The module item kinds are: {all_items:?}" + ))) + } else { + Err(de::Error::custom(format!( + "Not all module item kinds were part of the configured source ordering rule. \ + All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \ + The module item kinds are: {all_items:?}" + ))) + } + } +} + +impl Serialize for SourceItemOrderingModuleItemGroupings { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + self.groups.serialize(serializer) + } +} + +/// Represents all kinds of trait associated items. +#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SourceItemOrderingTraitAssocItemKind { + Const, + Fn, + Type, +} + +impl SourceItemOrderingTraitAssocItemKind { + pub fn all_variants() -> Vec { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingTraitAssocItemKind::*; + vec![Const, Fn, Type] + } +} + +/// Represents the order in which associated trait items should be ordered. +/// +/// The reason to wrap a `Vec` in a newtype is to be able to implement +/// [`Deserialize`]. Implementing `Deserialize` allows for implementing checks +/// on configuration completeness at the time of loading the clippy config, +/// letting the user know if there's any issues with the config (e.g. not +/// listing all item kinds that should be sorted). +#[derive(Clone)] +pub struct SourceItemOrderingTraitAssocItemKinds(Vec); + +impl SourceItemOrderingTraitAssocItemKinds { + pub fn index_of(&self, item: &SourceItemOrderingTraitAssocItemKind) -> Option { + self.0.iter().position(|i| i == item) + } +} + +impl From for SourceItemOrderingTraitAssocItemKinds +where + T: Into>, +{ + fn from(value: T) -> Self { + Self(value.into()) + } +} + +impl core::fmt::Debug for SourceItemOrderingTraitAssocItemKinds { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl<'de> Deserialize<'de> for SourceItemOrderingTraitAssocItemKinds { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let items = Vec::::deserialize(deserializer)?; + + let mut expected_items = SourceItemOrderingTraitAssocItemKind::all_variants(); + for item in &items { + expected_items.retain(|i| i != item); + } + + let all_items = SourceItemOrderingTraitAssocItemKind::all_variants(); + if expected_items.is_empty() && items.len() == all_items.len() { + Ok(Self(items)) + } else if items.len() != all_items.len() { + Err(de::Error::custom(format!( + "Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. \ + The trait associated item kinds are: {all_items:?}", + ))) + } else { + Err(de::Error::custom(format!( + "Not all trait associated item kinds were part of the configured source ordering rule. \ + All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \ + The trait associated item kinds are: {all_items:?}" + ))) + } + } +} + +impl Serialize for SourceItemOrderingTraitAssocItemKinds { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + self.0.serialize(serializer) + } +} + // these impls are never actually called but are used by the various config options that default to // empty lists macro_rules! unimplemented_serialize { diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index e15ba339717..8b32dc6b888 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 == TokenKind::Ident) { + while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == 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 == TokenKind::CloseBrace) + iter.find(|result| result.token_kind == TokenKind::CloseBrace) { last_decl_curly_offset = Some(range.end); } diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index d367fefec61..a2d1236629f 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -20,8 +20,14 @@ pub fn run(port: u16, lint: Option) -> ! { loop { let index_time = mtime("util/gh-pages/index.html"); + let times = [ + "clippy_lints/src", + "util/gh-pages/index_template.html", + "tests/compile-test.rs", + ] + .map(mtime); - if index_time < mtime("clippy_lints/src") || index_time < mtime("util/gh-pages/index_template.html") { + if times.iter().any(|&time| index_time < time) { Command::new(env::var("CARGO").unwrap_or("cargo".into())) .arg("collect-metadata") .spawn() diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs new file mode 100644 index 00000000000..8719f61a890 --- /dev/null +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -0,0 +1,531 @@ +use clippy_config::Conf; +use clippy_config::types::{ + SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, + SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, +}; +use clippy_utils::diagnostics::span_lint_and_note; +use rustc_hir::{ + AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, UseKind, + Variant, VariantData, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::impl_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// + /// Confirms that items are sorted in source files as per configuration. + /// + /// ### Why restrict this? + /// + /// Keeping a consistent ordering throughout the codebase helps with working + /// as a team, and possibly improves maintainability of the codebase. The + /// idea is that by defining a consistent and enforceable rule for how + /// source files are structured, less time will be wasted during reviews on + /// a topic that is (under most circumstances) not relevant to the logic + /// implemented in the code. Sometimes this will be referred to as + /// "bikeshedding". + /// + /// ### Default Ordering and Configuration + /// + /// As there is no generally applicable rule, and each project may have + /// different requirements, the lint can be configured with high + /// granularity. The configuration is split into two stages: + /// + /// 1. Which item kinds that should have an internal order enforced. + /// 2. Individual ordering rules per item kind. + /// + /// The item kinds that can be linted are: + /// - Module (with customized groupings, alphabetical within) + /// - Trait (with customized order of associated items, alphabetical within) + /// - Enum, Impl, Struct (purely alphabetical) + /// + /// #### Module Item Order + /// + /// Due to the large variation of items within modules, the ordering can be + /// configured on a very granular level. Item kinds can be grouped together + /// arbitrarily, items within groups will be ordered alphabetically. The + /// following table shows the default groupings: + /// + /// | Group | Item Kinds | + /// |--------------------|----------------------| + /// | `modules` | "mod", "foreign_mod" | + /// | `use` | "use" | + /// | `macros` | "macro" | + /// | `global_asm` | "global_asm" | + /// | `UPPER_SNAKE_CASE` | "static", "const" | + /// | `PascalCase` | "ty_alias", "opaque_ty", "enum", "struct", "union", "trait", "trait_alias", "impl" | + /// | `lower_snake_case` | "fn" | + /// + /// All item kinds must be accounted for to create an enforceable linting + /// rule set. + /// + /// ### Known Problems + /// + /// #### Performance Impact + /// + /// Keep in mind, that ordering source code alphabetically can lead to + /// reduced performance in cases where the most commonly used enum variant + /// isn't the first entry anymore, and similar optimizations that can reduce + /// branch misses, cache locality and such. Either don't use this lint if + /// that's relevant, or disable the lint in modules or items specifically + /// where it matters. Other solutions can be to use profile guided + /// optimization (PGO), post-link optimization (e.g. using BOLT for LLVM), + /// or other advanced optimization methods. A good starting point to dig + /// into optimization is [cargo-pgo][cargo-pgo]. + /// + /// #### Lints on a Contains basis + /// + /// The lint can be disabled only on a "contains" basis, but not per element + /// within a "container", e.g. the lint works per-module, per-struct, + /// per-enum, etc. but not for "don't order this particular enum variant". + /// + /// #### Module documentation + /// + /// Module level rustdoc comments are not part of the resulting syntax tree + /// and as such cannot be linted from within `check_mod`. Instead, the + /// `rustdoc::missing_documentation` lint may be used. + /// + /// #### Module Tests + /// + /// This lint does not implement detection of module tests (or other feature + /// dependent elements for that matter). To lint the location of mod tests, + /// the lint `items_after_test_module` can be used instead. + /// + /// ### Example + /// + /// ```no_run + /// trait TraitUnordered { + /// const A: bool; + /// const C: bool; + /// const B: bool; + /// + /// type SomeType; + /// + /// fn a(); + /// fn c(); + /// fn b(); + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// trait TraitOrdered { + /// const A: bool; + /// const B: bool; + /// const C: bool; + /// + /// type SomeType; + /// + /// fn a(); + /// fn b(); + /// fn c(); + /// } + /// ``` + /// + /// [cargo-pgo]: https://github.com/Kobzol/cargo-pgo/blob/main/README.md + /// + #[clippy::version = "1.82.0"] + pub ARBITRARY_SOURCE_ITEM_ORDERING, + restriction, + "arbitrary source item ordering" +} + +impl_lint_pass!(ArbitrarySourceItemOrdering => [ARBITRARY_SOURCE_ITEM_ORDERING]); + +#[derive(Debug)] +#[allow(clippy::struct_excessive_bools)] // Bools are cached feature flags. +pub struct ArbitrarySourceItemOrdering { + assoc_types_order: SourceItemOrderingTraitAssocItemKinds, + enable_ordering_for_enum: bool, + enable_ordering_for_impl: bool, + enable_ordering_for_module: bool, + enable_ordering_for_struct: bool, + enable_ordering_for_trait: bool, + module_item_order_groupings: SourceItemOrderingModuleItemGroupings, +} + +impl ArbitrarySourceItemOrdering { + pub fn new(conf: &'static Conf) -> Self { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingCategory::*; + Self { + assoc_types_order: conf.trait_assoc_item_kinds_order.clone(), + enable_ordering_for_enum: conf.source_item_ordering.contains(&Enum), + enable_ordering_for_impl: conf.source_item_ordering.contains(&Impl), + enable_ordering_for_module: conf.source_item_ordering.contains(&Module), + enable_ordering_for_struct: conf.source_item_ordering.contains(&Struct), + enable_ordering_for_trait: conf.source_item_ordering.contains(&Trait), + module_item_order_groupings: conf.module_item_order_groupings.clone(), + } + } + + /// Produces a linting warning for incorrectly ordered impl items. + fn lint_impl_item(&self, cx: &T, item: &ImplItemRef, before_item: &ImplItemRef) { + span_lint_and_note( + cx, + ARBITRARY_SOURCE_ITEM_ORDERING, + item.span, + format!( + "incorrect ordering of impl items (defined order: {:?})", + self.assoc_types_order + ), + Some(before_item.span), + format!("should be placed before `{}`", before_item.ident.as_str(),), + ); + } + + /// Produces a linting warning for incorrectly ordered item members. + fn lint_member_name( + cx: &T, + ident: &rustc_span::symbol::Ident, + before_ident: &rustc_span::symbol::Ident, + ) { + span_lint_and_note( + cx, + ARBITRARY_SOURCE_ITEM_ORDERING, + ident.span, + "incorrect ordering of items (must be alphabetically ordered)", + Some(before_ident.span), + format!("should be placed before `{}`", before_ident.as_str(),), + ); + } + + fn lint_member_item(cx: &T, item: &Item<'_>, before_item: &Item<'_>) { + let span = if item.ident.as_str().is_empty() { + &item.span + } else { + &item.ident.span + }; + + let (before_span, note) = if before_item.ident.as_str().is_empty() { + ( + &before_item.span, + "should be placed before the following item".to_owned(), + ) + } else { + ( + &before_item.ident.span, + format!("should be placed before `{}`", before_item.ident.as_str(),), + ) + }; + + // This catches false positives where generated code gets linted. + if span == before_span { + return; + } + + span_lint_and_note( + cx, + ARBITRARY_SOURCE_ITEM_ORDERING, + *span, + "incorrect ordering of items (must be alphabetically ordered)", + Some(*before_span), + note, + ); + } + + /// Produces a linting warning for incorrectly ordered trait items. + fn lint_trait_item(&self, cx: &T, item: &TraitItemRef, before_item: &TraitItemRef) { + span_lint_and_note( + cx, + ARBITRARY_SOURCE_ITEM_ORDERING, + item.span, + format!( + "incorrect ordering of trait items (defined order: {:?})", + self.assoc_types_order + ), + Some(before_item.span), + format!("should be placed before `{}`", before_item.ident.as_str(),), + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + match &item.kind { + ItemKind::Enum(enum_def, _generics) if self.enable_ordering_for_enum => { + let mut cur_v: Option<&Variant<'_>> = None; + for variant in enum_def.variants { + if in_external_macro(cx.sess(), variant.span) { + continue; + } + + if let Some(cur_v) = cur_v { + if cur_v.ident.name.as_str() > variant.ident.name.as_str() && cur_v.span != variant.span { + Self::lint_member_name(cx, &variant.ident, &cur_v.ident); + } + } + cur_v = Some(variant); + } + }, + ItemKind::Struct(VariantData::Struct { fields, .. }, _generics) if self.enable_ordering_for_struct => { + let mut cur_f: Option<&FieldDef<'_>> = None; + for field in *fields { + if in_external_macro(cx.sess(), field.span) { + continue; + } + + if let Some(cur_f) = cur_f { + if cur_f.ident.name.as_str() > field.ident.name.as_str() && cur_f.span != field.span { + Self::lint_member_name(cx, &field.ident, &cur_f.ident); + } + } + cur_f = Some(field); + } + }, + ItemKind::Trait(is_auto, _safety, _generics, _generic_bounds, item_ref) + if self.enable_ordering_for_trait && *is_auto == IsAuto::No => + { + let mut cur_t: Option<&TraitItemRef> = None; + + for item in *item_ref { + if in_external_macro(cx.sess(), item.span) { + continue; + } + + if let Some(cur_t) = cur_t { + let cur_t_kind = convert_assoc_item_kind(cur_t.kind); + let cur_t_kind_index = self.assoc_types_order.index_of(&cur_t_kind); + let item_kind = convert_assoc_item_kind(item.kind); + let item_kind_index = self.assoc_types_order.index_of(&item_kind); + + if cur_t_kind == item_kind && cur_t.ident.name.as_str() > item.ident.name.as_str() { + Self::lint_member_name(cx, &item.ident, &cur_t.ident); + } else if cur_t_kind_index > item_kind_index { + self.lint_trait_item(cx, item, cur_t); + } + } + cur_t = Some(item); + } + }, + ItemKind::Impl(trait_impl) if self.enable_ordering_for_impl => { + let mut cur_t: Option<&ImplItemRef> = None; + + for item in trait_impl.items { + if in_external_macro(cx.sess(), item.span) { + continue; + } + + if let Some(cur_t) = cur_t { + let cur_t_kind = convert_assoc_item_kind(cur_t.kind); + let cur_t_kind_index = self.assoc_types_order.index_of(&cur_t_kind); + let item_kind = convert_assoc_item_kind(item.kind); + let item_kind_index = self.assoc_types_order.index_of(&item_kind); + + if cur_t_kind == item_kind && cur_t.ident.name.as_str() > item.ident.name.as_str() { + Self::lint_member_name(cx, &item.ident, &cur_t.ident); + } else if cur_t_kind_index > item_kind_index { + self.lint_impl_item(cx, item, cur_t); + } + } + cur_t = Some(item); + } + }, + _ => {}, // Catch-all for `ItemKinds` that don't have fields. + } + } + + fn check_mod(&mut self, cx: &LateContext<'tcx>, module: &'tcx Mod<'tcx>, _: HirId) { + struct CurItem<'a> { + item: &'a Item<'a>, + order: usize, + name: String, + } + let mut cur_t: Option> = None; + + if !self.enable_ordering_for_module { + return; + } + + let items = module.item_ids.iter().map(|&id| cx.tcx.hir().item(id)); + + // Iterates over the items within a module. + // + // As of 2023-05-09, the Rust compiler will hold the entries in the same + // order as they appear in the source code, which is convenient for us, + // as no sorting by source map/line of code has to be applied. + // + for item in items { + if in_external_macro(cx.sess(), item.span) { + continue; + } + + // The following exceptions (skipping with `continue;`) may not be + // complete, edge cases have not been explored further than what + // appears in the existing code base. + if item.ident.name == rustc_span::symbol::kw::Empty { + if let ItemKind::Impl(_) = item.kind { + // Sorting trait impls for unnamed types makes no sense. + if get_item_name(item).is_empty() { + continue; + } + } else if let ItemKind::ForeignMod { .. } = item.kind { + continue; + } else if let ItemKind::GlobalAsm(_) = item.kind { + continue; + } else if let ItemKind::Use(path, use_kind) = item.kind { + if path.segments.is_empty() { + // Use statements that contain braces get caught here. + // They will still be linted internally. + continue; + } else if path.segments.len() >= 2 + && (path.segments[0].ident.name == rustc_span::sym::std + || path.segments[0].ident.name == rustc_span::sym::core) + && path.segments[1].ident.name == rustc_span::sym::prelude + { + // Filters the autogenerated prelude use statement. + // e.g. `use std::prelude::rustc_2021` + } else if use_kind == UseKind::Glob { + // Filters glob kinds of uses. + // e.g. `use std::sync::*` + } else { + // This can be used for debugging. + // println!("Unknown autogenerated use statement: {:?}", item); + } + continue; + } + } + + if item.ident.name.as_str().starts_with('_') { + // Filters out unnamed macro-like impls for various derives, + // e.g. serde::Serialize or num_derive::FromPrimitive. + continue; + } + + if item.ident.name == rustc_span::sym::std && item.span.is_dummy() { + if let ItemKind::ExternCrate(None) = item.kind { + // Filters the auto-included Rust standard library. + continue; + } + println!("Unknown item: {item:?}"); + } + + let item_kind = convert_module_item_kind(&item.kind); + let module_level_order = self + .module_item_order_groupings + .module_level_order_of(&item_kind) + .unwrap_or_default(); + + if let Some(cur_t) = cur_t.as_ref() { + use std::cmp::Ordering; // Better legibility. + match module_level_order.cmp(&cur_t.order) { + Ordering::Less => { + Self::lint_member_item(cx, item, cur_t.item); + }, + Ordering::Equal if item_kind == SourceItemOrderingModuleItemKind::Use => { + // Skip ordering use statements, as these should be ordered by rustfmt. + }, + Ordering::Equal if cur_t.name > get_item_name(item) => { + Self::lint_member_item(cx, item, cur_t.item); + }, + Ordering::Equal | Ordering::Greater => { + // Nothing to do in this case, they're already in the right order. + }, + } + } + + // Makes a note of the current item for comparison with the next. + cur_t = Some(CurItem { + order: module_level_order, + item, + name: get_item_name(item), + }); + } + } +} + +/// Converts a [`rustc_hir::AssocItemKind`] to a +/// [`SourceItemOrderingTraitAssocItemKind`]. +/// +/// This is implemented here because `rustc_hir` is not a dependency of +/// `clippy_config`. +fn convert_assoc_item_kind(value: AssocItemKind) -> SourceItemOrderingTraitAssocItemKind { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingTraitAssocItemKind::*; + match value { + AssocItemKind::Const { .. } => Const, + AssocItemKind::Type { .. } => Type, + AssocItemKind::Fn { .. } => Fn, + } +} + +/// Converts a [`rustc_hir::ItemKind`] to a +/// [`SourceItemOrderingModuleItemKind`]. +/// +/// This is implemented here because `rustc_hir` is not a dependency of +/// `clippy_config`. +fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleItemKind { + #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. + use SourceItemOrderingModuleItemKind::*; + match value { + ItemKind::ExternCrate(..) => ExternCrate, + ItemKind::Use(..) => Use, + ItemKind::Static(..) => Static, + ItemKind::Const(..) => Const, + ItemKind::Fn(..) => Fn, + ItemKind::Macro(..) => Macro, + ItemKind::Mod(..) => Mod, + ItemKind::ForeignMod { .. } => ForeignMod, + ItemKind::GlobalAsm(..) => GlobalAsm, + ItemKind::TyAlias(..) => TyAlias, + ItemKind::Enum(..) => Enum, + ItemKind::Struct(..) => Struct, + ItemKind::Union(..) => Union, + ItemKind::Trait(..) => Trait, + ItemKind::TraitAlias(..) => TraitAlias, + ItemKind::Impl(..) => Impl, + } +} + +/// Gets the item name for sorting purposes, which in the general case is +/// `item.ident.name`. +/// +/// For trait impls, the name used for sorting will be the written path of +/// `item.self_ty` plus the written path of `item.of_trait`, joined with +/// exclamation marks. Exclamation marks are used because they are the first +/// printable ASCII character. +/// +/// Trait impls generated using a derive-macro will have their path rewritten, +/// such that for example `Default` is `$crate::default::Default`, and +/// `std::clone::Clone` is `$crate::clone::Clone`. This behaviour is described +/// further in the [Rust Reference, Paths Chapter][rust_ref]. +/// +/// [rust_ref]: https://doc.rust-lang.org/reference/paths.html#crate-1 +fn get_item_name(item: &Item<'_>) -> String { + match item.kind { + ItemKind::Impl(im) => { + if let TyKind::Path(path) = im.self_ty.kind { + match path { + QPath::Resolved(_, path) => { + let segs = path.segments.iter(); + let mut segs: Vec = segs.map(|s| s.ident.name.as_str().to_owned()).collect(); + + if let Some(of_trait) = im.of_trait { + let mut trait_segs: Vec = of_trait + .path + .segments + .iter() + .map(|s| s.ident.name.as_str().to_owned()) + .collect(); + segs.append(&mut trait_segs); + } + + segs.push(String::new()); + segs.join("!!") + }, + QPath::TypeRelative(_, _path_seg) => { + // This case doesn't exist in the clippy tests codebase. + String::new() + }, + QPath::LangItem(_, _) => String::new(), + } + } else { + // Impls for anything that isn't a named type can be skipped. + String::new() + } + }, + _ => item.ident.name.as_str().to_owned(), + } +} diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 684756ce87f..c29c9f08cf7 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -14,7 +14,7 @@ use clippy_config::Conf; use clippy_config::msrvs::{self, Msrv}; -use rustc_ast::{Attribute, MetaItemInner, MetaItemKind, self as ast}; +use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind}; use rustc_hir::{ImplItem, Item, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 92b9f9cba52..dc5a6857089 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -1,10 +1,9 @@ -use super::utils::{extract_clippy_lint, is_lint_level, is_word}; use super::USELESS_ATTRIBUTE; +use super::utils::{extract_clippy_lint, is_lint_level, is_word}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{SpanRangeExt, first_line_of_span}; -use rustc_ast::MetaItemInner; +use rustc_ast::{Attribute, Item, ItemKind, MetaItemInner}; use rustc_errors::Applicability; -use rustc_ast::{Item, ItemKind, Attribute}; use rustc_lint::{EarlyContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_span::sym; diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 9952d0af99e..6948cf560a5 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -15,7 +15,7 @@ /// `MutexGuard`. /// /// ### Why is this bad? - /// The Mutex types found in [`std::sync`][https://doc.rust-lang.org/stable/std/sync/] and + /// The Mutex types found in [`std::sync`](https://doc.rust-lang.org/stable/std/sync/) and /// [`parking_lot`](https://docs.rs/parking_lot/latest/parking_lot/) are /// not designed to operate in an async context across await points. /// diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 26888f7e3a0..26a20bc99a0 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -203,6 +203,21 @@ fn check_simplify_not(cx: &LateContext<'_>, msrv: &Msrv, expr: &Expr<'_>) { && let Some(suggestion) = simplify_not(cx, msrv, inner) && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow { + use clippy_utils::sugg::{Sugg, has_enclosing_paren}; + let maybe_par = if let Some(sug) = Sugg::hir_opt(cx, inner) { + match sug { + Sugg::BinOp(..) => true, + Sugg::MaybeParen(sug) if !has_enclosing_paren(&sug) => true, + _ => false, + } + } else { + false + }; + let suggestion = if maybe_par { + format!("({suggestion})") + } else { + suggestion + }; span_lint_and_sugg( cx, NONMINIMAL_BOOL, diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index cba8224b84c..f2551a05b1a 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -4,7 +4,7 @@ use clippy_utils::ty::implements_trait; use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed}; use rustc_errors::Applicability; -use rustc_hir::{ExprKind, UnOp}; +use rustc_hir::{BorrowKind, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::Mutability; use rustc_middle::ty; @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { - if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, addrof_target) = e.kind && let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..)) && !e.span.from_expansion() diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index b11b967f8bc..205357afd84 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind { - if method_path.ident.name == sym!(cast) + if method_path.ident.name.as_str() == "cast" && let Some(generic_args) = method_path.args && let [GenericArg::Type(cast_to)] = generic_args.args // There probably is no obvious reason to do this, just to be consistent with `as` cases. diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 811d33c8bde..abd80abffe6 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -268,8 +268,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx if !snippet .split("->") .skip(1) - .map(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from))) - .any(|a| a) + .any(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from))) { return ControlFlow::Break(()); } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 2e7f91a842e..f76e399517c 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -232,7 +232,7 @@ fn get_types_from_cast<'a>( // or `to_type::MAX as from_type` let call_from_cast: Option<(&Expr<'_>, &str)> = if let ExprKind::Cast(limit, from_type) = &expr.kind // to_type::max_value(), from_type - && let TyKind::Path(ref from_type_path) = &from_type.kind + && let TyKind::Path(from_type_path) = &from_type.kind && let Some(from_sym) = int_ty_to_sym(from_type_path) { Some((limit, from_sym)) @@ -245,7 +245,7 @@ fn get_types_from_cast<'a>( if let ExprKind::Call(from_func, [limit]) = &expr.kind // `from_type::from, to_type::max_value()` // `from_type::from` - && let ExprKind::Path(ref path) = &from_func.kind + && let ExprKind::Path(path) = &from_func.kind && let Some(from_sym) = get_implementing_type(path, INTS, "from") { Some((limit, from_sym)) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index d90a22bb62a..c4afdc757d8 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -540,24 +540,22 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo .iter() .filter(|&&(_, name)| !name.as_str().starts_with('_')) .any(|&(_, name)| { - let mut walker = ContainsName { - name, - result: false, - cx, - }; + let mut walker = ContainsName { name, cx }; // Scan block - block + let mut res = block .stmts .iter() .filter(|stmt| !ignore_span.overlaps(stmt.span)) - .for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); + .try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); if let Some(expr) = block.expr { - intravisit::walk_expr(&mut walker, expr); + if res.is_continue() { + res = intravisit::walk_expr(&mut walker, expr); + } } - walker.result + res.is_break() }) }) } diff --git a/clippy_lints/src/copy_iterator.rs b/clippy_lints/src/copy_iterator.rs index 50fd76a3a47..4ecf3e41611 100644 --- a/clippy_lints/src/copy_iterator.rs +++ b/clippy_lints/src/copy_iterator.rs @@ -37,7 +37,7 @@ impl<'tcx> LateLintPass<'tcx> for CopyIterator { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), + of_trait: Some(trait_ref), .. }) = item.kind && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() diff --git a/clippy_lints/src/ctfe.rs b/clippy_lints/src/ctfe.rs index 181514e8372..589b99518a0 100644 --- a/clippy_lints/src/ctfe.rs +++ b/clippy_lints/src/ctfe.rs @@ -5,7 +5,6 @@ use rustc_session::declare_lint_pass; use rustc_span::Span; - declare_lint_pass! { /// Ensures that Constant-time Function Evaluation is being done (specifically, MIR lint passes). /// As Clippy deactivates codegen, this lint ensures that CTFE (used in hard errors) is still ran. diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 3c4e75df8ab..edb52851e0c 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -28,12 +28,15 @@ #[cfg(feature = "internal")] crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO, #[cfg(feature = "internal")] + crate::utils::internal_lints::slow_symbol_comparisons::SLOW_SYMBOL_COMPARISONS_INFO, + #[cfg(feature = "internal")] crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS_INFO, crate::absolute_paths::ABSOLUTE_PATHS_INFO, crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO, crate::approx_const::APPROX_CONSTANT_INFO, + crate::arbitrary_source_item_ordering::ARBITRARY_SOURCE_ITEM_ORDERING_INFO, crate::arc_with_non_send_sync::ARC_WITH_NON_SEND_SYNC_INFO, crate::as_conversions::AS_CONVERSIONS_INFO, crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO, @@ -414,14 +417,17 @@ crate::methods::MANUAL_SPLIT_ONCE_INFO, crate::methods::MANUAL_STR_REPEAT_INFO, crate::methods::MANUAL_TRY_FOLD_INFO, + crate::methods::MAP_ALL_ANY_IDENTITY_INFO, crate::methods::MAP_CLONE_INFO, crate::methods::MAP_COLLECT_RESULT_UNIT_INFO, crate::methods::MAP_ERR_IGNORE_INFO, crate::methods::MAP_FLATTEN_INFO, crate::methods::MAP_IDENTITY_INFO, crate::methods::MAP_UNWRAP_OR_INFO, + crate::methods::MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES_INFO, crate::methods::MUT_MUTEX_LOCK_INFO, crate::methods::NAIVE_BYTECOUNT_INFO, + crate::methods::NEEDLESS_AS_BYTES_INFO, crate::methods::NEEDLESS_CHARACTER_ITERATION_INFO, crate::methods::NEEDLESS_COLLECT_INFO, crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO, diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 2920bbb4c81..2b6bfafe695 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -187,7 +187,7 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex impl<'tcx> LateLintPass<'tcx> for DerivableImpls { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), + of_trait: Some(trait_ref), items: [child], self_ty, .. diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index e8e21edd494..e569c4dc786 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths}; @@ -202,7 +204,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), + of_trait: Some(trait_ref), .. }) = item.kind { @@ -371,9 +373,8 @@ fn check_unsafe_derive_deserialize<'tcx>( ty: Ty<'tcx>, ) { fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { - let mut visitor = UnsafeVisitor { cx, has_unsafe: false }; - walk_item(&mut visitor, item); - visitor.has_unsafe + let mut visitor = UnsafeVisitor { cx }; + walk_item(&mut visitor, item).is_break() } if let Some(trait_def_id) = trait_ref.trait_def_id() @@ -406,38 +407,37 @@ fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { struct UnsafeVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - has_unsafe: bool, } impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; type NestedFilter = nested_filter::All; - fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: LocalDefId) { - if self.has_unsafe { - return; - } - + fn visit_fn( + &mut self, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body_id: BodyId, + _: Span, + id: LocalDefId, + ) -> Self::Result { if let Some(header) = kind.header() && header.safety == Safety::Unsafe { - self.has_unsafe = true; + ControlFlow::Break(()) + } else { + walk_fn(self, kind, decl, body_id, id) } - - walk_fn(self, kind, decl, body_id, id); } - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.has_unsafe { - return; - } - + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { if let ExprKind::Block(block, _) = expr.kind { if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { - self.has_unsafe = true; + return ControlFlow::Break(()); } } - walk_expr(self, expr); + walk_expr(self, expr) } fn nested_visit_map(&mut self) -> Self::Map { diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs index f79264e6b04..53c24a3faf1 100644 --- a/clippy_lints/src/disallowed_script_idents.rs +++ b/clippy_lints/src/disallowed_script_idents.rs @@ -80,7 +80,7 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { let mut symbols: Vec<_> = symbols.iter().collect(); symbols.sort_unstable_by_key(|k| k.1); - for (symbol, &span) in &symbols { + for &(symbol, &span) in &symbols { // Note: `symbol.as_str()` is an expensive operation, thus should not be called // more than once for a single symbol. let symbol_str = symbol.as_str(); diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 89c6a4e08dc..df7c37a192a 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -427,11 +427,11 @@ declare_clippy_lint! { /// ### What it does - /// Checks if the first line in the documentation of items listed in module page is too long. + /// Checks if the first paragraph in the documentation of items listed in the 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. + /// Documentation will show the first paragraph of the docstring in the summary page of a + /// module. Having a nice, short summary in the first paragraph is part of writing good docs. /// /// ### Example /// ```no_run @@ -453,7 +453,7 @@ #[clippy::version = "1.82.0"] pub TOO_LONG_FIRST_DOC_PARAGRAPH, nursery, - "ensure that the first line of a documentation paragraph isn't too long" + "ensure the first documentation paragraph is short" } declare_clippy_lint! { diff --git a/clippy_lints/src/empty_drop.rs b/clippy_lints/src/empty_drop.rs index b66dd2108fc..10a84b1b2ff 100644 --- a/clippy_lints/src/empty_drop.rs +++ b/clippy_lints/src/empty_drop.rs @@ -36,7 +36,7 @@ impl LateLintPass<'_> for EmptyDrop { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), + of_trait: Some(trait_ref), items: [child], .. }) = item.kind diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 6ca599ed361..de10b7bf533 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -9,8 +9,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ - self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, - TypeckResults, + self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults, }; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; @@ -204,11 +203,16 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc // '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(cx.typing_mode()).err_ctxt().type_implements_fn_trait( - cx.param_env, - Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ty::PredicatePolarity::Positive, - ) { + match cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .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() => { diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 4e4434ec7d1..0550c22761a 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -58,7 +58,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // match call to write_fmt && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind) && let ExprKind::Call(write_recv_path, []) = write_recv.kind - && write_fun.ident.name == sym!(write_fmt) + && write_fun.ident.name.as_str() == "write_fmt" && let Some(def_id) = path_def_id(cx, write_recv_path) { // match calls to std::io::stdout() / std::io::stderr () diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index d5a2e06863d..14da0b515a5 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use clippy_config::Conf; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; @@ -115,25 +117,26 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { } /// Finds the occurrences of `Self` and `self` +/// +/// Returns `ControlFlow::break` if any of the `self`/`Self` usages were from an expansion, or the +/// body contained a binding already named `val`. struct SelfFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, /// Occurrences of `Self` upper: Vec, /// Occurrences of `self` lower: Vec, - /// If any of the `self`/`Self` usages were from an expansion, or the body contained a binding - /// already named `val` - invalid: bool, } impl<'tcx> Visitor<'tcx> for SelfFinder<'_, 'tcx> { + type Result = ControlFlow<()>; type NestedFilter = OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } - fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) { + fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) -> Self::Result { for segment in path.segments { match segment.ident.name { kw::SelfLower => self.lower.push(segment.ident.span), @@ -141,17 +144,19 @@ fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) { _ => continue, } - self.invalid |= segment.ident.span.from_expansion(); + if segment.ident.span.from_expansion() { + return ControlFlow::Break(()); + } } - if !self.invalid { - walk_path(self, path); - } + walk_path(self, path) } - fn visit_name(&mut self, name: Symbol) { + fn visit_name(&mut self, name: Symbol) -> Self::Result { if name == sym::val { - self.invalid = true; + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } } } @@ -209,11 +214,9 @@ fn convert_to_from( cx, upper: Vec::new(), lower: Vec::new(), - invalid: false, }; - finder.visit_expr(body.value); - if finder.invalid { + if finder.visit_expr(body.value).is_break() { return None; } diff --git a/clippy_lints/src/from_raw_with_void_ptr.rs b/clippy_lints/src/from_raw_with_void_ptr.rs index d62d008d480..c8828c93615 100644 --- a/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/clippy_lints/src/from_raw_with_void_ptr.rs @@ -41,7 +41,7 @@ impl LateLintPass<'_> for FromRawWithVoidPtr { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(box_from_raw, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind - && seg.ident.name == sym!(from_raw) + && seg.ident.name.as_str() == "from_raw" && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) && let arg_kind = cx.typeck_results().expr_ty(arg).kind() && let ty::RawPtr(ty, _) = arg_kind diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 91d73c2a9c9..be3d0f7ad63 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -441,7 +441,7 @@ /// fn bar(&self) -> Option<&String> { None } /// # } /// ``` - #[clippy::version = "1.82.0"] + #[clippy::version = "1.83.0"] pub REF_OPTION, pedantic, "function signature uses `&Option` instead of `Option<&T>`" diff --git a/clippy_lints/src/functions/ref_option.rs b/clippy_lints/src/functions/ref_option.rs index 373ecd74cb3..aba0fbcb9fe 100644 --- a/clippy_lints/src/functions/ref_option.rs +++ b/clippy_lints/src/functions/ref_option.rs @@ -15,9 +15,9 @@ fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { if let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() && is_type_diagnostic_item(cx, *opt_ty, sym::Option) - && let ty::Adt(_, opt_gen) = opt_ty.kind() - && let [gen] = opt_gen.as_slice() - && let GenericArgKind::Type(gen_ty) = gen.unpack() + && let ty::Adt(_, opt_gen_args) = opt_ty.kind() + && let [gen_arg] = opt_gen_args.as_slice() + && let GenericArgKind::Type(gen_ty) = gen_arg.unpack() && !gen_ty.is_ref() // Need to gen the original spans, so first parsing mid, and hir parsing afterward && let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 4c5375730b8..c370f206c8f 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -340,7 +340,7 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) { if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) { if method.ident.name == sym::new { self.suggestions.insert(e.span, "HashMap::default()".to_string()); - } else if method.ident.name == sym!(with_capacity) { + } else if method.ident.name.as_str() == "with_capacity" { self.suggestions.insert( e.span, format!( @@ -352,7 +352,7 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) { } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) { if method.ident.name == sym::new { self.suggestions.insert(e.span, "HashSet::default()".to_string()); - } else if method.ident.name == sym!(with_capacity) { + } else if method.ident.name.as_str() == "with_capacity" { self.suggestions.insert( e.span, format!( diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 71c317552ee..48874d6064b 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -157,7 +157,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { .and(cap); } } - if method.ident.name == sym!(flat_map) && args.len() == 1 { + if method.ident.name.as_str() == "flat_map" && args.len() == 1 { if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind { let body = cx.tcx.hir().body(body); return is_infinite(cx, body.value); @@ -224,7 +224,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { return MaybeInfinite.and(is_infinite(cx, receiver)); } } - if method.ident.name == sym!(last) && args.is_empty() { + if method.ident.name.as_str() == "last" && args.is_empty() { let not_double_ended = cx .tcx .get_diagnostic_item(sym::DoubleEndedIterator) @@ -234,7 +234,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { if not_double_ended { return is_infinite(cx, receiver); } - } else if method.ident.name == sym!(collect) { + } else if method.ident.name.as_str() == "collect" { let ty = cx.typeck_results().expr_ty(expr); if matches!( get_type_diagnostic_name(cx, ty), diff --git a/clippy_lints/src/iter_without_into_iter.rs b/clippy_lints/src/iter_without_into_iter.rs index 1e6404190d3..314d0dfa26c 100644 --- a/clippy_lints/src/iter_without_into_iter.rs +++ b/clippy_lints/src/iter_without_into_iter.rs @@ -142,7 +142,7 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) { ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some() }) && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| { - if item.ident.name == sym!(IntoIter) { + if item.ident.name.as_str() == "IntoIter" { Some(cx.tcx.hir().impl_item(item.id).expect_type().span) } else { None @@ -247,8 +247,8 @@ fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<' let sugg = format!( " impl IntoIterator for {self_ty_snippet} {{ - type IntoIter = {ret_ty}; type Item = {iter_ty}; + type IntoIter = {ret_ty}; fn into_iter(self) -> Self::IntoIter {{ self.iter() }} diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 5d2b521b250..aa8b4d88d2f 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -4,7 +4,7 @@ use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, ConstKind}; +use rustc_middle::ty::{self, ParamEnv}; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Pos, Span}; @@ -56,7 +56,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { && !item.span.from_expansion() && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && let ty::Array(element_type, cst) = ty.kind() - && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind() + && let Ok((_, ty::ValTree::Leaf(element_count))) = cst.eval_valtree(cx.tcx, ParamEnv::empty(), item.span) && let element_count = element_count.to_target_usize(cx.tcx) && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index f2f841dcec3..ab3d19f89c3 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -1,11 +1,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; -use rustc_ast::LitKind; +use clippy_utils::source::snippet_opt; +use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::sym; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -51,6 +52,24 @@ pub fn new(conf: &'static Conf) -> Self { impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]); +impl LargeIncludeFile { + fn emit_lint(&self, cx: &LateContext<'_>, span: Span) { + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( + cx, + LARGE_INCLUDE_FILE, + span, + "attempted to include a large file", + |diag| { + diag.note(format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + )); + }, + ); + } +} + impl LateLintPass<'_> for LargeIncludeFile { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Lit(lit) = &expr.kind @@ -66,19 +85,33 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { && (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)) { - #[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", - |diag| { - diag.note(format!( - "the configuration allows a maximum size of {} bytes", - self.max_file_size - )); - }, - ); + self.emit_lint(cx, expr.span.source_callsite()); + } + } + + fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) { + if !attr.span.from_expansion() + // Currently, rustc limits the usage of macro at the top-level of attributes, + // so we don't need to recurse into each level. + && let AttrKind::Normal(ref normal) = attr.kind + && let AttrArgs::Eq(_, AttrArgsEq::Hir(ref meta)) = normal.item.args + && !attr.span.contains(meta.span) + // Since the `include_str` is already expanded at this point, we can only take the + // whole attribute snippet and then modify for our suggestion. + && let Some(snippet) = snippet_opt(cx, attr.span) + // We cannot remove this because a `#[doc = include_str!("...")]` attribute can + // occupy several lines. + && let Some(start) = snippet.find('[') + && let Some(end) = snippet.rfind(']') + && let snippet = &snippet[start + 1..end] + // We check that the expansion actually comes from `include_str!` and not just from + // another macro. + && let Some(sub_snippet) = snippet.trim().strip_prefix("doc") + && let Some(sub_snippet) = sub_snippet.trim().strip_prefix("=") + && let sub_snippet = sub_snippet.trim() + && (sub_snippet.starts_with("include_str!") || sub_snippet.starts_with("include_bytes!")) + { + self.emit_lint(cx, attr.span); } } } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 47c65ee6d0b..b7887ef76a5 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -629,7 +629,7 @@ fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { .filter_by_name_unhygienic(is_empty) .any(|item| is_is_empty(cx, item)) }), - ty::Alias(ty::Projection, ref proj) => has_is_empty_impl(cx, proj.def_id), + ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), ty::Adt(id, _) => has_is_empty_impl(cx, id.did()), ty::Array(..) | ty::Slice(..) | ty::Str => true, _ => false, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3fd07ced0e4..69cc14a8fcc 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -73,6 +73,7 @@ mod absolute_paths; mod almost_complete_range; mod approx_const; +mod arbitrary_source_item_ordering; mod arc_with_non_send_sync; mod as_conversions; mod asm_syntax; @@ -606,6 +607,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| { Box::new(utils::internal_lints::almost_standard_lint_formulation::AlmostStandardFormulation::new()) }); + store.register_late_pass(|_| Box::new(utils::internal_lints::slow_symbol_comparisons::SlowSymbolComparisons)); } store.register_late_pass(|_| Box::new(ctfe::ClippyCtfe)); @@ -953,5 +955,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); + store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 119e410b91c..ee561ea85ed 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -29,11 +29,7 @@ pub(super) fn check( if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) { return; } - } else if count - .try_to_target_usize(cx.tcx) - .map_or(true, |x| x > 32) - && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN) - { + } else if count.try_to_target_usize(cx.tcx).map_or(true, |x| x > 32) && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN) { return; } } diff --git a/clippy_lints/src/loops/infinite_loop.rs b/clippy_lints/src/loops/infinite_loop.rs index e25c03db534..9f543b79bac 100644 --- a/clippy_lints/src/loops/infinite_loop.rs +++ b/clippy_lints/src/loops/infinite_loop.rs @@ -1,12 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{fn_def_id, is_from_proc_macro, is_lint_allowed}; use hir::intravisit::{Visitor, walk_expr}; -use hir::{Expr, ExprKind, FnRetTy, FnSig, Node}; +use hir::{Expr, ExprKind, FnRetTy, FnSig, Node, TyKind}; use rustc_ast::Label; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_span::sym; use super::INFINITE_LOOP; @@ -25,13 +26,7 @@ pub(super) fn check<'tcx>( return; }; // Or, its parent function is already returning `Never` - if matches!( - parent_fn_ret, - FnRetTy::Return(hir::Ty { - kind: hir::TyKind::Never, - .. - }) - ) { + if is_never_return(parent_fn_ret) { return; } @@ -69,6 +64,16 @@ pub(super) fn check<'tcx>( fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option> { for (_, parent_node) in cx.tcx.hir().parent_iter(expr.hir_id) { match parent_node { + // Skip `Coroutine` closures, these are the body of `async fn`, not async closures. + // This is because we still need to backtrack one parent node to get the `OpaqueDef` ty. + Node::Expr(Expr { + kind: + ExprKind::Closure(hir::Closure { + kind: hir::ClosureKind::Coroutine(_), + .. + }), + .. + }) => (), Node::Item(hir::Item { kind: hir::ItemKind::Fn(FnSig { decl, .. }, _, _), .. @@ -143,3 +148,41 @@ fn visit_expr(&mut self, ex: &'hir Expr<'_>) { } } } + +/// Return `true` if the given [`FnRetTy`] is never (!). +/// +/// Note: This function also take care of return type of async fn, +/// as the actual type is behind an [`OpaqueDef`](TyKind::OpaqueDef). +fn is_never_return(ret_ty: FnRetTy<'_>) -> bool { + let FnRetTy::Return(hir_ty) = ret_ty else { return false }; + + match hir_ty.kind { + TyKind::Never => true, + TyKind::OpaqueDef(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::AsyncFn { .. }, + bounds, + .. + }) => { + if let Some(trait_ref) = bounds.iter().find_map(|b| b.trait_ref()) + && let Some(segment) = trait_ref + .path + .segments + .iter() + .find(|seg| seg.ident.name == sym::future_trait) + && let Some(args) = segment.args + && let Some(cst_kind) = args + .constraints + .iter() + .find_map(|cst| (cst.ident.name == sym::Output).then_some(cst.kind)) + && let hir::AssocItemConstraintKind::Equality { + term: hir::Term::Ty(ty), + } = cst_kind + { + matches!(ty.kind, TyKind::Never) + } else { + false + } + }, + _ => false, + } +} diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index 1a1cde3c5bd..7da4fa76e2c 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -19,10 +19,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &' cx, ids: HirIdSet::default(), def_ids: DefIdMap::default(), - skip: false, }; - var_visitor.visit_expr(cond); - if var_visitor.skip { + if var_visitor.visit_expr(cond).is_break() { return; } let used_in_condition = &var_visitor.ids; @@ -81,7 +79,6 @@ struct VarCollectorVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, ids: HirIdSet, def_ids: DefIdMap, - skip: bool, } impl<'tcx> VarCollectorVisitor<'_, 'tcx> { @@ -104,11 +101,15 @@ fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) { } impl<'tcx> Visitor<'tcx> for VarCollectorVisitor<'_, 'tcx> { - fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { + type Result = ControlFlow<()>; + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) -> Self::Result { match ex.kind { - ExprKind::Path(_) => self.insert_def_id(ex), + ExprKind::Path(_) => { + self.insert_def_id(ex); + ControlFlow::Continue(()) + }, // If there is any function/method call… we just stop analysis - ExprKind::Call(..) | ExprKind::MethodCall(..) => self.skip = true, + ExprKind::Call(..) | ExprKind::MethodCall(..) => ControlFlow::Break(()), _ => walk_expr(self, ex), } diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 74467522619..b7e37c1a876 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; @@ -204,35 +206,32 @@ fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tc struct V<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, iter_expr: &'b IterExpr, - uses_iter: bool, } impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> { - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if self.uses_iter { - // return - } else if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { - self.uses_iter = true; + type Result = ControlFlow<()>; + fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { + if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { + ControlFlow::Break(()) } else if let (e, true) = skip_fields_and_path(e) { if let Some(e) = e { - self.visit_expr(e); + self.visit_expr(e) + } else { + ControlFlow::Continue(()) } } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind { if is_res_used(self.cx, self.iter_expr.path, id) { - self.uses_iter = true; + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } } else { - walk_expr(self, e); + walk_expr(self, e) } } } - let mut v = V { - cx, - iter_expr, - uses_iter: false, - }; - v.visit_expr(container); - v.uses_iter + let mut v = V { cx, iter_expr }; + v.visit_expr(container).is_break() } #[expect(clippy::too_many_lines)] @@ -242,34 +241,38 @@ struct AfterLoopVisitor<'a, 'b, 'tcx> { iter_expr: &'b IterExpr, loop_id: HirId, after_loop: bool, - used_iter: bool, } impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { type NestedFilter = OnlyBodies; + type Result = ControlFlow<()>; fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if self.used_iter { - return; - } + fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { if self.after_loop { if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { - self.used_iter = true; + ControlFlow::Break(()) } else if let (e, true) = skip_fields_and_path(e) { if let Some(e) = e { - self.visit_expr(e); + self.visit_expr(e) + } else { + ControlFlow::Continue(()) } } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind { - self.used_iter = is_res_used(self.cx, self.iter_expr.path, id); + if is_res_used(self.cx, self.iter_expr.path, id) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } } else { - walk_expr(self, e); + walk_expr(self, e) } } else if self.loop_id == e.hir_id { self.after_loop = true; + ControlFlow::Continue(()) } else { - walk_expr(self, e); + walk_expr(self, e) } } } @@ -347,9 +350,8 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) { iter_expr, loop_id: loop_expr.hir_id, after_loop: false, - used_iter: false, }; - v.visit_expr(cx.tcx.hir().body(cx.enclosing_body.unwrap()).value); - v.used_iter + v.visit_expr(cx.tcx.hir().body(cx.enclosing_body.unwrap()).value) + .is_break() } } diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index c904137da1a..3c77db84a40 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -106,13 +106,12 @@ fn check_fn( fn future_trait_ref<'tcx>(cx: &LateContext<'tcx>, opaque: &'tcx OpaqueTy<'tcx>) -> Option<&'tcx TraitRef<'tcx>> { if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { - if let GenericBound::Trait(poly) = bound { - Some(&poly.trait_ref) - } else { - None - } - }) - && trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait() + if let GenericBound::Trait(poly) = bound { + Some(&poly.trait_ref) + } else { + None + } + }) && trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait() { return Some(trait_ref); } @@ -156,16 +155,18 @@ fn captures_all_lifetimes(cx: &LateContext<'_>, fn_def_id: LocalDefId, opaque_de .tcx .opaque_captured_lifetimes(opaque_def_id) .iter() - .filter(|&(lifetime, _)| match *lifetime { - ResolvedArg::EarlyBound(_) | ResolvedArg::LateBound(ty::INNERMOST, _, _) => true, - _ => false, + .filter(|&(lifetime, _)| { + matches!( + *lifetime, + ResolvedArg::EarlyBound(_) | ResolvedArg::LateBound(ty::INNERMOST, _, _) + ) }) .count(); num_captured_lifetimes == num_early_lifetimes + num_late_lifetimes } fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { - if let Some(Expr { + if let Some(&Expr { kind: ExprKind::Closure(&Closure { kind, body, .. }), .. }) = block.expr diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 1bd8813e348..fd71167f814 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -7,8 +7,7 @@ use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::sym; @@ -53,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind && let BinOpKind::Mul = &bin_op.node - && !in_external_macro(cx.sess(), expr.span) + && !expr.span.from_expansion() && self.msrv.meets(msrvs::MANUAL_BITS) && let ctxt = expr.span.ctxt() && left_expr.span.ctxt() == ctxt diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index c62bd65bea6..7a9c9963742 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -68,7 +68,7 @@ fn check_local(&mut self, cx: &LateContext<'_>, local: &LetStmt<'_>) { && let Some(init) = local.init && !init.span.from_expansion() && let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind - && seg.ident.name == sym!(build_hasher) + && seg.ident.name.as_str() == "build_hasher" && let Node::Stmt(local_stmt) = cx.tcx.parent_hir_node(local.hir_id) && let Node::Block(block) = cx.tcx.parent_hir_node(local_stmt.hir_id) @@ -96,7 +96,7 @@ fn check_local(&mut self, cx: &LateContext<'_>, local: &LetStmt<'_>) { && let Node::Expr(finish_expr) = cx.tcx.parent_hir_node(path_expr.hir_id) && !finish_expr.span.from_expansion() && let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind - && seg.ident.name == sym!(finish) + && seg.ident.name.as_str() == "finish" && self.msrv.meets(msrvs::BUILD_HASHER_HASH_ONE) { diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 24207705743..dec8c5d85de 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -105,7 +105,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { check_is_ascii(cx, macro_call.span, recv, &range, None); } } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind - && path.ident.name == sym!(contains) + && path.ident.name.as_str() == "contains" && let Some(higher::Range { start: Some(start), end: Some(end), diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 64cb7a06ce9..6d62b530ae7 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -148,7 +148,7 @@ fn find_bool_lit(ex: &ExprKind<'_>) -> Option { }) => Some(*b), ExprKind::Block( rustc_hir::Block { - stmts: &[], + stmts: [], expr: Some(exp), .. }, diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index 463aa602bc8..1267fc9d0a5 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; @@ -23,11 +25,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arm if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind() && let ty::Str = ty.kind() { - let mut visitor = MatchExprVisitor { cx, case_method: None }; - - visitor.visit_expr(scrutinee); - - if let Some(case_method) = visitor.case_method { + let mut visitor = MatchExprVisitor { cx }; + if let ControlFlow::Break(case_method) = visitor.visit_expr(scrutinee) { if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); } @@ -37,30 +36,33 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arm struct MatchExprVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - case_method: Option, } impl<'tcx> Visitor<'tcx> for MatchExprVisitor<'_, 'tcx> { - fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { - match ex.kind { - ExprKind::MethodCall(segment, receiver, [], _) if self.case_altered(segment.ident.as_str(), receiver) => {}, - _ => walk_expr(self, ex), + type Result = ControlFlow; + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) -> Self::Result { + if let ExprKind::MethodCall(segment, receiver, [], _) = ex.kind { + let result = self.case_altered(segment.ident.as_str(), receiver); + if result.is_break() { + return result; + } } + + walk_expr(self, ex) } } impl MatchExprVisitor<'_, '_> { - fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool { + fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> ControlFlow { if let Some(case_method) = get_case_method(segment_ident) { let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); if is_type_lang_item(self.cx, ty, LangItem::String) || ty.kind() == &ty::Str { - self.case_method = Some(case_method); - return true; + return ControlFlow::Break(case_method); } } - false + ControlFlow::Continue(()) } } diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index 42d9efe4ff6..9e54475033c 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -10,7 +10,7 @@ use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, sym}; use std::borrow::Cow; use std::ops::ControlFlow; @@ -95,7 +95,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: } else if let ExprKind::MethodCall(path, recv, args, ..) = guard.kind && let Some(binding) = get_pat_binding(cx, recv, outer_arm) { - check_method_calls(cx, outer_arm, path.ident.name, recv, args, guard, &binding); + check_method_calls(cx, outer_arm, path.ident.name.as_str(), recv, args, guard, &binding); } } } @@ -103,7 +103,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: fn check_method_calls<'tcx>( cx: &LateContext<'tcx>, arm: &Arm<'tcx>, - method: Symbol, + method: &str, recv: &Expr<'_>, args: &[Expr<'_>], if_expr: &Expr<'_>, @@ -112,7 +112,7 @@ fn check_method_calls<'tcx>( let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let slice_like = ty.is_slice() || ty.is_array(); - let sugg = if method == sym!(is_empty) { + let sugg = if method == "is_empty" { // `s if s.is_empty()` becomes "" // `arr if arr.is_empty()` becomes [] @@ -137,9 +137,9 @@ fn check_method_calls<'tcx>( if needles.is_empty() { sugg.insert_str(1, ".."); - } else if method == sym!(starts_with) { + } else if method == "starts_with" { sugg.insert_str(sugg.len() - 1, ", .."); - } else if method == sym!(ends_with) { + } else if method == "ends_with" { sugg.insert_str(1, ".., "); } else { return; diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 9ca75fb2615..ca45ed6ea59 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -319,10 +319,9 @@ fn found_good_method<'tcx>( node: (&PatKind<'_>, &PatKind<'_>), ) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> { match node { - ( - PatKind::TupleStruct(ref path_left, patterns_left, _), - PatKind::TupleStruct(ref path_right, patterns_right, _), - ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { + (PatKind::TupleStruct(path_left, patterns_left, _), PatKind::TupleStruct(path_right, patterns_right, _)) + if patterns_left.len() == 1 && patterns_right.len() == 1 => + { if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { find_good_method_for_match( cx, @@ -350,8 +349,8 @@ fn found_good_method<'tcx>( None } }, - (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right)) - | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _)) + (PatKind::TupleStruct(path_left, patterns, _), PatKind::Path(path_right)) + | (PatKind::Path(path_left), PatKind::TupleStruct(path_right, patterns, _)) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { @@ -381,14 +380,14 @@ fn found_good_method<'tcx>( None } }, - (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Wild) if patterns.len() == 1 => { + (PatKind::TupleStruct(path_left, patterns, _), PatKind::Wild) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { get_good_method(cx, arms, path_left) } else { None } }, - (PatKind::Path(ref path_left), PatKind::Wild) => get_good_method(cx, arms, path_left), + (PatKind::Path(path_left), PatKind::Wild) => get_good_method(cx, arms, path_left), _ => None, } } diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 047d070a131..2aea25b5f12 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -247,7 +247,10 @@ fn get_std_enum_variant<'tcx>( 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))); + *self = Self::StdEnum( + cx.arena + .alloc_from_iter(std::iter::repeat_with(|| Self::Other).take(adt.variants().len())), + ); let Self::StdEnum(x) = self else { unreachable!(); }; diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 06482a743dd..c1653b65e98 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -21,8 +21,8 @@ fn is_method(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol) -> bool ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name, ExprKind::Path(QPath::Resolved(_, segments)) => segments.segments.last().unwrap().ident.name == method_name, ExprKind::MethodCall(segment, _, _, _) => segment.ident.name == method_name, - ExprKind::Closure(&Closure { body, .. }) => { - let body = cx.tcx.hir().body(body); + ExprKind::Closure(Closure { body, .. }) => { + let body = cx.tcx.hir().body(*body); let closure_expr = peel_blocks(body.value); match closure_expr.kind { ExprKind::MethodCall(PathSegment { ident, .. }, receiver, ..) => { @@ -234,12 +234,12 @@ fn hir(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, filter_param_id: HirId) - // the latter only calls `effect` once let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span); - if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym!(is_some) { + if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name.as_str() == "is_some" { Some(Self::IsSome { receiver, side_effect_expr_span, }) - } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym!(is_ok) { + } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name.as_str() == "is_ok" { Some(Self::IsOk { receiver, side_effect_expr_span, diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs index cc82f6cfd63..a0c21faaa4c 100644 --- a/clippy_lints/src/methods/is_empty.rs +++ b/clippy_lints/src/methods/is_empty.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{find_binding_init, path_to_local}; +use clippy_utils::macros::{is_assert_macro, root_macro_call}; +use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context, path_to_local}; use rustc_hir::{Expr, HirId}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; @@ -14,6 +15,16 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ if in_external_macro(cx.sess(), expr.span) || !receiver.span.eq_ctxt(expr.span) { return; } + if let Some(parent) = get_parent_expr(cx, expr) { + if let Some(parent) = get_parent_expr(cx, parent) { + if is_inside_always_const_context(cx.tcx, expr.hir_id) + && let Some(macro_call) = root_macro_call(parent.span) + && is_assert_macro(cx, macro_call.def_id) + { + return; + } + } + } let init_expr = expr_or_init(cx, receiver); if !receiver.span.eq_ctxt(init_expr.span) { return; diff --git a/clippy_lints/src/methods/iter_out_of_bounds.rs b/clippy_lints/src/methods/iter_out_of_bounds.rs index 9d462bd1845..9a62b719a8f 100644 --- a/clippy_lints/src/methods/iter_out_of_bounds.rs +++ b/clippy_lints/src/methods/iter_out_of_bounds.rs @@ -29,10 +29,7 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) -> if cx.tcx.is_diagnostic_item(sym::ArrayIntoIter, did) { // For array::IntoIter, the length is the second generic // parameter. - substs - .const_at(1) - .try_to_target_usize(cx.tcx) - .map(u128::from) + substs.const_at(1).try_to_target_usize(cx.tcx).map(u128::from) } else if cx.tcx.is_diagnostic_item(sym::SliceIter, did) && let ExprKind::MethodCall(_, recv, ..) = iter.kind { diff --git a/clippy_lints/src/methods/map_all_any_identity.rs b/clippy_lints/src/methods/map_all_any_identity.rs new file mode 100644 index 00000000000..ac11baa2d54 --- /dev/null +++ b/clippy_lints/src/methods/map_all_any_identity.rs @@ -0,0 +1,43 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::{is_expr_identity_function, is_trait_method}; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::{Span, sym}; + +use super::MAP_ALL_ANY_IDENTITY; + +#[allow(clippy::too_many_arguments)] +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + map_call_span: Span, + map_arg: &Expr<'_>, + any_call_span: Span, + any_arg: &Expr<'_>, + method: &str, +) { + if is_trait_method(cx, expr, sym::Iterator) + && is_trait_method(cx, recv, sym::Iterator) + && is_expr_identity_function(cx, any_arg) + && let map_any_call_span = map_call_span.with_hi(any_call_span.hi()) + && let Some(map_arg) = map_arg.span.get_source_text(cx) + { + span_lint_and_then( + cx, + MAP_ALL_ANY_IDENTITY, + map_any_call_span, + format!("usage of `.map(...).{method}(identity)`"), + |diag| { + diag.span_suggestion_verbose( + map_any_call_span, + format!("use `.{method}(...)` instead"), + format!("{method}({map_arg})"), + Applicability::MachineApplicable, + ); + }, + ); + } +} diff --git a/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs new file mode 100644 index 00000000000..fc656fd78ba --- /dev/null +++ b/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs @@ -0,0 +1,134 @@ +use crate::methods::MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES; +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use clippy_utils::{eager_or_lazy, higher, usage}; +use rustc_ast::LitKind; +use rustc_ast::ast::RangeLimits; +use rustc_data_structures::packed::Pu128; +use rustc_errors::Applicability; +use rustc_hir::{Body, Closure, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Span; + +fn extract_count_with_applicability( + cx: &LateContext<'_>, + range: higher::Range<'_>, + applicability: &mut Applicability, +) -> Option { + let start = range.start?; + let end = range.end?; + // TODO: This doens't handle if either the start or end are negative literals, or if the start is + // not a literal. In the first case, we need to be careful about how we handle computing the + // count to avoid overflows. In the second, we may need to add parenthesis to make the + // suggestion correct. + if let ExprKind::Lit(lit) = start.kind + && let LitKind::Int(Pu128(lower_bound), _) = lit.node + { + if let ExprKind::Lit(lit) = end.kind + && let LitKind::Int(Pu128(upper_bound), _) = lit.node + { + // Here we can explicitly calculate the number of iterations + let count = if upper_bound >= lower_bound { + match range.limits { + RangeLimits::HalfOpen => upper_bound - lower_bound, + RangeLimits::Closed => (upper_bound - lower_bound).checked_add(1)?, + } + } else { + 0 + }; + return Some(format!("{count}")); + } + let end_snippet = Sugg::hir_with_applicability(cx, end, "...", applicability) + .maybe_par() + .into_string(); + if lower_bound == 0 { + if range.limits == RangeLimits::Closed { + return Some(format!("{end_snippet} + 1")); + } + return Some(end_snippet); + } + if range.limits == RangeLimits::Closed { + return Some(format!("{end_snippet} - {}", lower_bound - 1)); + } + return Some(format!("{end_snippet} - {lower_bound}")); + } + None +} + +pub(super) fn check( + cx: &LateContext<'_>, + ex: &Expr<'_>, + receiver: &Expr<'_>, + arg: &Expr<'_>, + msrv: &Msrv, + method_call_span: Span, +) { + let mut applicability = Applicability::MaybeIncorrect; + if let Some(range) = higher::Range::hir(receiver) + && let ExprKind::Closure(Closure { body, .. }) = arg.kind + && let body_hir = cx.tcx.hir().body(*body) + && let Body { + params: [param], + value: body_expr, + } = body_hir + && !usage::BindingUsageFinder::are_params_used(cx, body_hir) + && let Some(count) = extract_count_with_applicability(cx, range, &mut applicability) + { + let method_to_use_name; + let new_span; + let use_take; + + if eager_or_lazy::switch_to_eager_eval(cx, body_expr) { + if msrv.meets(msrvs::REPEAT_N) { + method_to_use_name = "repeat_n"; + let body_snippet = snippet_with_applicability(cx, body_expr.span, "..", &mut applicability); + new_span = (arg.span, format!("{body_snippet}, {count}")); + use_take = false; + } else { + method_to_use_name = "repeat"; + let body_snippet = snippet_with_applicability(cx, body_expr.span, "..", &mut applicability); + new_span = (arg.span, body_snippet.to_string()); + use_take = true; + } + } else if msrv.meets(msrvs::REPEAT_WITH) { + method_to_use_name = "repeat_with"; + new_span = (param.span, String::new()); + use_take = true; + } else { + return; + } + + // We need to provide nonempty parts to diag.multipart_suggestion so we + // collate all our parts here and then remove those that are empty. + let mut parts = vec![ + ( + receiver.span.to(method_call_span), + format!("std::iter::{method_to_use_name}"), + ), + new_span, + ]; + if use_take { + parts.push((ex.span.shrink_to_hi(), format!(".take({count})"))); + } + + span_lint_and_then( + cx, + MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, + ex.span, + "map of a closure that does not depend on its parameter over a range", + |diag| { + diag.multipart_suggestion( + if use_take { + format!("remove the explicit range and use `{method_to_use_name}` and `take`") + } else { + format!("remove the explicit range and use `{method_to_use_name}`") + }, + parts, + applicability, + ); + }, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2a391870d70..b1809796355 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -60,13 +60,16 @@ mod manual_saturating_arithmetic; mod manual_str_repeat; mod manual_try_fold; +mod map_all_any_identity; mod map_clone; mod map_collect_result_unit; mod map_err_ignore; mod map_flatten; mod map_identity; mod map_unwrap_or; +mod map_with_unused_argument_over_ranges; mod mut_mutex_lock; +mod needless_as_bytes; mod needless_character_iteration; mod needless_collect; mod needless_option_as_deref; @@ -4166,6 +4169,90 @@ "calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`" } +declare_clippy_lint! { + /// ### What it does + /// It detects useless calls to `str::as_bytes()` before calling `len()` or `is_empty()`. + /// + /// ### Why is this bad? + /// The `len()` and `is_empty()` methods are also directly available on strings, and they + /// return identical results. In particular, `len()` on a string returns the number of + /// bytes. + /// + /// ### Example + /// ``` + /// let len = "some string".as_bytes().len(); + /// let b = "some string".as_bytes().is_empty(); + /// ``` + /// Use instead: + /// ``` + /// let len = "some string".len(); + /// let b = "some string".is_empty(); + /// ``` + #[clippy::version = "1.84.0"] + pub NEEDLESS_AS_BYTES, + complexity, + "detect useless calls to `as_bytes()`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `.map(…)`, followed by `.all(identity)` or `.any(identity)`. + /// + /// ### Why is this bad? + /// The `.all(…)` or `.any(…)` methods can be called directly in place of `.map(…)`. + /// + /// ### Example + /// ``` + /// # let mut v = [""]; + /// let e1 = v.iter().map(|s| s.is_empty()).all(|a| a); + /// let e2 = v.iter().map(|s| s.is_empty()).any(std::convert::identity); + /// ``` + /// Use instead: + /// ``` + /// # let mut v = [""]; + /// let e1 = v.iter().all(|s| s.is_empty()); + /// let e2 = v.iter().any(|s| s.is_empty()); + /// ``` + #[clippy::version = "1.84.0"] + pub MAP_ALL_ANY_IDENTITY, + complexity, + "combine `.map(_)` followed by `.all(identity)`/`.any(identity)` into a single call" +} + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `Iterator::map` over ranges without using the parameter which + /// could be more clearly expressed using `std::iter::repeat(...).take(...)` + /// or `std::iter::repeat_n`. + /// + /// ### Why is this bad? + /// + /// It expresses the intent more clearly to `take` the correct number of times + /// from a generating function than to apply a closure to each number in a + /// range only to discard them. + /// + /// ### Example + /// + /// ```no_run + /// let random_numbers : Vec<_> = (0..10).map(|_| { 3 + 1 }).collect(); + /// ``` + /// Use instead: + /// ```no_run + /// let f : Vec<_> = std::iter::repeat( 3 + 1 ).take(10).collect(); + /// ``` + /// + /// ### Known Issues + /// + /// This lint may suggest replacing a `Map` with a `Take`. + /// The former implements some traits that the latter does not, such as + /// `DoubleEndedIterator`. + #[clippy::version = "1.84.0"] + pub MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, + restriction, + "map of a trivial closure (not dependent on parameter) over a range" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4327,6 +4414,9 @@ pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { NEEDLESS_CHARACTER_ITERATION, MANUAL_INSPECT, UNNECESSARY_MIN_OR_MAX, + NEEDLESS_AS_BYTES, + MAP_ALL_ANY_IDENTITY, + MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4534,15 +4624,21 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { ("all", [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); needless_character_iteration::check(cx, expr, recv, arg, true); - if let Some(("cloned", recv2, [], _, _)) = method_call(recv) { - iter_overeager_cloned::check( - cx, - expr, - recv, - recv2, - iter_overeager_cloned::Op::NeedlessMove(arg), - false, - ); + match method_call(recv) { + Some(("cloned", recv2, [], _, _)) => { + iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::NeedlessMove(arg), + false, + ); + }, + Some(("map", _, [map_arg], _, map_call_span)) => { + map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "all"); + }, + _ => {}, } }, ("and_then", [arg]) => { @@ -4571,6 +4667,9 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { { string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); }, + Some(("map", _, [map_arg], _, map_call_span)) => { + map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "any"); + }, _ => {}, } }, @@ -4764,8 +4863,14 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { unit_hash::check(cx, expr, recv, arg); }, ("is_empty", []) => { - if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { - redundant_as_str::check(cx, expr, recv, as_str_span, span); + match method_call(recv) { + Some(("as_bytes", prev_recv, [], _, _)) => { + needless_as_bytes::check(cx, "is_empty", recv, prev_recv, expr.span); + }, + Some(("as_str", recv, [], as_str_span, _)) => { + redundant_as_str::check(cx, expr, recv, as_str_span, span); + }, + _ => {}, } is_empty::check(cx, expr, recv); }, @@ -4795,6 +4900,11 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { ); } }, + ("len", []) => { + if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) { + needless_as_bytes::check(cx, "len", recv, prev_recv, expr.span); + } + }, ("lock", []) => { mut_mutex_lock::check(cx, expr, recv, span); }, @@ -4802,6 +4912,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if name == "map" { unused_enumerate_index::check(cx, expr, recv, m_arg); map_clone::check(cx, expr, recv, m_arg, &self.msrv); + map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, &self.msrv, span); match method_call(recv) { Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => { iter_kv_map::check(cx, map_name, expr, recv2, m_arg, &self.msrv); @@ -5182,7 +5293,6 @@ fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { } #[rustfmt::skip] -#[expect(clippy::large_const_arrays, reason = "`Span` is not sync, so this can't be static")] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), diff --git a/clippy_lints/src/methods/needless_as_bytes.rs b/clippy_lints/src/methods/needless_as_bytes.rs new file mode 100644 index 00000000000..75e9f317230 --- /dev/null +++ b/clippy_lints/src/methods/needless_as_bytes.rs @@ -0,0 +1,28 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_type_lang_item; +use rustc_errors::Applicability; +use rustc_hir::{Expr, LangItem}; +use rustc_lint::LateContext; +use rustc_span::Span; + +use super::NEEDLESS_AS_BYTES; + +pub fn check(cx: &LateContext<'_>, method: &str, recv: &Expr<'_>, prev_recv: &Expr<'_>, span: Span) { + if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() + && let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs() + && (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str()) + { + let mut app = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app); + span_lint_and_sugg( + cx, + NEEDLESS_AS_BYTES, + span, + "needless call to `as_bytes()`", + format!("`{method}()` can be called directly on strings"), + format!("{sugg}.{method}()"), + app, + ); + } +} diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 96a31812ca4..055107068ae 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -322,7 +322,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { // Check function calls on our collection if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind { if args.is_empty() - && method_name.ident.name == sym!(collect) + && method_name.ident.name.as_str() == "collect" && is_trait_method(self.cx, expr, sym::Iterator) { self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv)); diff --git a/clippy_lints/src/methods/read_line_without_trim.rs b/clippy_lints/src/methods/read_line_without_trim.rs index 65e545ed03a..db2b9d4d92f 100644 --- a/clippy_lints/src/methods/read_line_without_trim.rs +++ b/clippy_lints/src/methods/read_line_without_trim.rs @@ -44,7 +44,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< if let Some(parent) = get_parent_expr(cx, expr) { let data = if let ExprKind::MethodCall(segment, recv, args, span) = parent.kind { if args.is_empty() - && segment.ident.name == sym!(parse) + && segment.ident.name.as_str() == "parse" && let parse_result_ty = cx.typeck_results().expr_ty(parent) && is_type_diagnostic_item(cx, parse_result_ty, sym::Result) && let ty::Adt(_, substs) = parse_result_ty.kind() @@ -58,7 +58,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< "calling `.parse()` on a string without trimming the trailing newline character", "checking", )) - } else if segment.ident.name == sym!(ends_with) + } else if segment.ident.name.as_str() == "ends_with" && recv.span == expr.span && let [arg] = args && expr_is_string_literal_without_trailing_newline(arg) diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index ca46da81fa0..bab439015c5 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,13 +1,15 @@ use super::utils::clone_or_copy_needed; -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; +use clippy_utils::{MaybePath, is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; use core::ops::ControlFlow; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; +use rustc_middle::query::Key; use rustc_middle::ty; use rustc_span::sym; @@ -36,9 +38,25 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a ControlFlow::Continue(Descend::Yes) } }); - let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); let sugg = if !found_filtering { + // Check if the closure is .filter_map(|x| Some(x)) + if name == "filter_map" + && let hir::ExprKind::Call(expr, args) = body.value.kind + && is_res_lang_ctor(cx, path_res(cx, expr), OptionSome) + && arg_id.ty_def_id() == args[0].hir_id().ty_def_id() + && let hir::ExprKind::Path(_) = args[0].kind + { + span_lint_and_sugg( + cx, + UNNECESSARY_FILTER_MAP, + expr.span, + format!("{name} is unnecessary"), + "try removing the filter_map", + String::new(), + Applicability::MaybeIncorrect, + ); + } if name == "filter_map" { "map" } else { "map(..).next()" } } else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) { match cx.typeck_results().expr_ty(body.value).kind() { @@ -52,7 +70,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a } else { return; }; - span_lint( + span_lint_and_sugg( cx, if name == "filter_map" { UNNECESSARY_FILTER_MAP @@ -60,7 +78,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a UNNECESSARY_FIND_MAP }, expr.span, - format!("this `.{name}` can be written more simply using `.{sugg}`"), + format!("this `.{name}` can be written more simply"), + "try instead", + sugg.to_string(), + Applicability::MaybeIncorrect, ); } } @@ -78,7 +99,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc (true, true) }, hir::ExprKind::MethodCall(segment, recv, [arg], _) => { - if segment.ident.name == sym!(then_some) + if segment.ident.name.as_str() == "then_some" && cx.typeck_results().expr_ty(recv).is_bool() && path_to_local_id(arg, arg_id) { diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index 6911da69b94..a92fe9e55a4 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -5,7 +5,8 @@ use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, GenericArgKind}; +use rustc_middle::ty; +use rustc_middle::ty::GenericArgKind; use rustc_span::sym; use rustc_span::symbol::Ident; use std::iter; diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index e95864c6db8..ed89b3b3438 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -79,9 +79,9 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { - if path.ident.name == sym!(max) { + if path.ident.name.as_str() == "max" { fetch_const(cx, Some(receiver), args, MinMax::Max) - } else if path.ident.name == sym!(min) { + } else if path.ident.name.as_str() == "min" { fetch_const(cx, Some(receiver), args, MinMax::Min) } else { None diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index ce508d85d63..1300c7d1062 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -12,11 +12,13 @@ use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::Visibility; use rustc_session::impl_lint_pass; use rustc_span::def_id::CRATE_DEF_ID; +use rustc_span::symbol::kw; use rustc_span::{Span, sym}; declare_clippy_lint! { @@ -110,6 +112,21 @@ fn check_missing_docs_attrs( return; } + if let Some(parent_def_id) = cx.tcx.opt_parent(def_id.to_def_id()) + && let DefKind::AnonConst + | DefKind::AssocConst + | DefKind::AssocFn + | DefKind::Closure + | DefKind::Const + | DefKind::Fn + | DefKind::InlineConst + | DefKind::Static { .. } + | DefKind::SyntheticCoroutineBody = cx.tcx.def_kind(parent_def_id) + { + // Nested item has no generated documentation, so it doesn't need to be documented. + return; + } + let has_doc = attrs .iter() .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())) @@ -184,8 +201,12 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { } } }, - hir::ItemKind::Const(..) - | hir::ItemKind::Enum(..) + hir::ItemKind::Const(..) => { + if it.ident.name == kw::Underscore { + note_prev_span_then_ret!(self.prev_span, it.span); + } + }, + hir::ItemKind::Enum(..) | hir::ItemKind::Macro(..) | hir::ItemKind::Mod(..) | hir::ItemKind::Static(..) diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index fc01b6753f1..f28b431ab99 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -116,7 +116,7 @@ fn should_lint<'tcx>( if path.ident.name == sym::debug_struct && is_type_diagnostic_item(cx, recv_ty, sym::Formatter) { has_debug_struct = true; - } else if path.ident.name == sym!(finish_non_exhaustive) + } else if path.ident.name.as_str() == "finish_non_exhaustive" && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) { has_finish_non_exhaustive = true; diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 152635a5c35..e589b3608b3 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -96,10 +96,6 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { self.found = true; return; }, - ExprKind::If(..) => { - self.found = true; - return; - }, ExprKind::Path(_) => { if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { if adj diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index ce97370d4d9..c4836772933 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -330,10 +330,11 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin } fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind + if let ast::ExprKind::Loop(loop_block, loop_label, ..) = &expr.kind && let Some(last_stmt) = loop_block.stmts.last() && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind - && let ast::ExprKind::Continue(_) = inner_expr.kind + && let ast::ExprKind::Continue(continue_label) = inner_expr.kind + && compare_labels(loop_label.as_ref(), continue_label.as_ref()) { span_lint_and_help( cx, diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 46cdb82130f..a67addea948 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -347,7 +347,7 @@ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { if let LetStmt { init: None, pat: - &Pat { + Pat { kind: PatKind::Binding(BindingMode::NONE, binding_id, _, None), .. }, @@ -357,7 +357,7 @@ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { && let Some((_, Node::Stmt(local_stmt))) = parents.next() && let Some((_, Node::Block(block))) = parents.next() { - check(cx, local, local_stmt, block, binding_id); + check(cx, local, local_stmt, block, *binding_id); } } } diff --git a/clippy_lints/src/needless_maybe_sized.rs b/clippy_lints/src/needless_maybe_sized.rs index 9a1c397b5b2..852a0ce8c6d 100644 --- a/clippy_lints/src/needless_maybe_sized.rs +++ b/clippy_lints/src/needless_maybe_sized.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::def_id::{DefId, DefIdMap}; -use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, BoundPolarity, WherePredicate}; +use rustc_hir::{BoundPolarity, GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, WherePredicate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ClauseKind, PredicatePolarity}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index f8c815d9729..b7dc269061c 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -183,7 +183,7 @@ fn check_fn( .iter() .zip(fn_sig.inputs()) .zip(body.params) - .filter(|((&input, &ty), arg)| !should_skip(cx, input, ty, arg)) + .filter(|&((&input, &ty), arg)| !should_skip(cx, input, ty, arg)) .peekable(); if it.peek().is_none() { return; diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 2e5195d459f..74536028b5d 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -159,8 +159,12 @@ fn check_no_effect(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { // Remove `impl Future` to get `T` if cx.tcx.ty_is_opaque_future(ret_ty) - && let Some(true_ret_ty) = - cx.tcx.infer_ctxt().build(cx.typing_mode()).err_ctxt().get_impl_future_output_ty(ret_ty) + && let Some(true_ret_ty) = cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .err_ctxt() + .get_impl_future_output_ty(ret_ty) { ret_ty = true_ret_ty; } diff --git a/clippy_lints/src/no_mangle_with_rust_abi.rs b/clippy_lints/src/no_mangle_with_rust_abi.rs index 8d5a523fd8f..64587e08ddc 100644 --- a/clippy_lints/src/no_mangle_with_rust_abi.rs +++ b/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{snippet, snippet_with_applicability}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -18,13 +18,13 @@ /// Rust ABI can break this at any point. /// /// ### Example - /// ```no_run + /// ```rust,ignore /// #[no_mangle] /// fn example(arg_one: u32, arg_two: usize) {} /// ``` /// /// Use instead: - /// ```no_run + /// ```rust,ignore /// #[no_mangle] /// extern "C" fn example(arg_one: u32, arg_two: usize) {} /// ``` @@ -40,24 +40,25 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if let ItemKind::Fn(fn_sig, _, _) = &item.kind { let attrs = cx.tcx.hir().attrs(item.hir_id()); let mut app = Applicability::MaybeIncorrect; - let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut app); + let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(item.ident.span.lo()), "..", &mut app); for attr in attrs { if let Some(ident) = attr.ident() && ident.name == rustc_span::sym::no_mangle && fn_sig.header.abi == Abi::Rust - && let Some((fn_attrs, _)) = snippet.split_once("fn") + && let Some((fn_attrs, _)) = fn_snippet.rsplit_once("fn") && !fn_attrs.contains("extern") { let sugg_span = fn_sig .span .with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len())) .shrink_to_lo(); + let attr_snippet = snippet(cx, attr.span, ".."); span_lint_and_then( cx, NO_MANGLE_WITH_RUST_ABI, fn_sig.span, - "`#[no_mangle]` set on a function with the default (`Rust`) ABI", + format!("`{attr_snippet}` set on a function with the default (`Rust`) ABI"), |diag| { diag.span_suggestion(sugg_span, "set an ABI", "extern \"C\" ", app) .span_suggestion(sugg_span, "or explicitly set the default", "extern \"Rust\" ", app); diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 3f156aa5510..0caa19cd844 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -43,12 +43,12 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { match &expr.kind { ExprKind::MethodCall(path, func, [param], _) => { if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def() - && ((path.ident.name == sym!(mode) + && ((path.ident.name.as_str() == "mode" && matches!( cx.tcx.get_diagnostic_name(adt.did()), Some(sym::FsOpenOptions | sym::DirBuilder) )) - || (path.ident.name == sym!(set_mode) + || (path.ident.name.as_str() == "set_mode" && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()))) && let ExprKind::Lit(_) = param.kind && param.span.eq_ctxt(expr.span) diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index 8e394944c21..ab5f91c1d67 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -107,7 +107,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } if let ExprKind::MethodCall(method_name, self_arg, [], _) = expr.kind - && sym!(signum) == method_name.ident.name + && method_name.ident.name.as_str() == "signum" // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) { diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 18e6aad9c9a..794bef7b321 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -34,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), + of_trait: Some(trait_ref), items: impl_items, .. }) = item.kind diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 56d07aeae17..808a7e005c6 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -96,7 +96,7 @@ fn expr_as_ptr_offset_call<'tcx>( if path_segment.ident.name == sym::offset { return Some((arg_0, arg_1, Method::Offset)); } - if path_segment.ident.name == sym!(wrapping_offset) { + if path_segment.ident.name.as_str() == "wrapping_offset" { return Some((arg_0, arg_1, Method::WrappingOffset)); } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 9344cb41993..cb374fcf20e 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -93,7 +93,7 @@ enum IfBlockType<'hir> { fn find_let_else_ret_expression<'hir>(block: &'hir Block<'hir>) -> Option<&'hir Expr<'hir>> { if let Block { - stmts: &[], + stmts: [], expr: Some(els), .. } = block @@ -163,8 +163,8 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ is_type_diagnostic_item(cx, caller_ty, smbl) && expr_return_none_or_err(smbl, cx, if_then, caller, None) && match smbl { - sym::Option => call_sym == sym!(is_none), - sym::Result => call_sym == sym!(is_err), + sym::Option => call_sym.as_str() == "is_none", + sym::Result => call_sym.as_str() == "is_err", _ => false, } }, diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 19ce08bde10..7d59bf24d93 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { if let TyKind::Ref(_, ref mut_ty) = ty.kind && mut_ty.mutbl == Mutability::Not - && let TyKind::Path(ref qpath) = &mut_ty.ty.kind + && let TyKind::Path(qpath) = &mut_ty.ty.kind && let last = last_path_segment(qpath) && let Some(def_id) = last.res.opt_def_id() && cx.tcx.is_diagnostic_item(sym::Option, def_id) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 110dea8fb8e..1e0f6dff1ab 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -347,7 +347,7 @@ fn check_final_expr<'tcx>( let peeled_drop_expr = expr.peel_drop_temps(); match &peeled_drop_expr.kind { // simple return is always "bad" - ExprKind::Ret(ref inner) => { + ExprKind::Ret(inner) => { // check if expr return nothing let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 09f1c112352..d85f4a8fa01 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -101,17 +101,21 @@ fn semicolon_inside_block(&self, cx: &LateContext<'_>, block: &Block<'_>, tail: ); } - fn semicolon_outside_block( - &self, - cx: &LateContext<'_>, - block: &Block<'_>, - tail_stmt_expr: &Expr<'_>, - semi_span: Span, - ) { + fn semicolon_outside_block(&self, cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>) { let insert_span = block.span.with_lo(block.span.hi()); - // account for macro calls - let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span); - let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi()); + + // For macro call semicolon statements (`mac!();`), the statement's span does not actually + // include the semicolon itself, so use `mac_call_stmt_semi_span`, which finds the semicolon + // based on a source snippet. + // (Does not use `stmt_span` as that requires `.from_expansion()` to return true, + // which is not the case for e.g. `line!();` and `asm!();`) + let Some(remove_span) = cx + .sess() + .source_map() + .mac_call_stmt_semi_span(tail_stmt_expr.span.source_callsite()) + else { + return; + }; if self.semicolon_outside_block_ignore_multiline && get_line(cx, remove_span) != get_line(cx, insert_span) { return; @@ -150,13 +154,12 @@ fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { }; let &Stmt { kind: StmtKind::Semi(expr), - span, .. } = stmt else { return; }; - self.semicolon_outside_block(cx, block, expr, span); + self.semicolon_outside_block(cx, block, expr); }, StmtKind::Semi(Expr { kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _), diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs index fcf1c4cc5c2..6a0dfde2d9c 100644 --- a/clippy_lints/src/serde_api.rs +++ b/clippy_lints/src/serde_api.rs @@ -26,7 +26,7 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), + of_trait: Some(trait_ref), items, .. }) = item.kind diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 9fdee8543a8..fa082453504 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -174,7 +174,7 @@ fn track_uses( } match &item.kind { - ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { + ItemKind::Mod(_, ModKind::Loaded(items, ..)) => { self.check_mod(items); }, ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index ec6e45256d1..8c8f11569c8 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -235,7 +235,7 @@ fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind && path_to_local_id(self_arg, self.vec_alloc.local_id) - && path.ident.name == sym!(extend) + && path.ident.name.as_str() == "extend" && self.is_repeat_take(extend_arg) { self.slow_expression = Some(InitializationType::Extend(expr)); @@ -247,7 +247,7 @@ fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'tcx>) { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind && path_to_local_id(self_arg, self.vec_alloc.local_id) - && path.ident.name == sym!(resize) + && path.ident.name.as_str() == "resize" // Check that is filled with 0 && is_integer_literal(fill_arg, 0) { @@ -269,7 +269,7 @@ fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'tcx>) { /// Returns `true` if give expression is `repeat(0).take(...)` fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool { if let ExprKind::MethodCall(take_path, recv, [len_arg], _) = expr.kind - && take_path.ident.name == sym!(take) + && take_path.ident.name.as_str() == "take" // Check that take is applied to `repeat(0)` && self.is_repeat_zero(recv) { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index bf49bb60162..e09c0706006 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -286,7 +286,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if !in_external_macro(cx.sess(), e.span) && let ExprKind::MethodCall(path, receiver, ..) = &e.kind - && path.ident.name == sym!(as_bytes) + && path.ident.name.as_str() == "as_bytes" && let ExprKind::Lit(lit) = &receiver.kind && let LitKind::Str(lit_content, _) = &lit.node { @@ -332,7 +332,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { } if let ExprKind::MethodCall(path, recv, [], _) = &e.kind - && path.ident.name == sym!(into_bytes) + && path.ident.name.as_str() == "into_bytes" && let ExprKind::MethodCall(path, recv, [], _) = &recv.kind && matches!(path.ident.name.as_str(), "to_owned" | "to_string") && let ExprKind::Lit(lit) = &recv.kind @@ -493,7 +493,7 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { let tyckres = cx.typeck_results(); if let ExprKind::MethodCall(path, split_recv, [], split_ws_span) = expr.kind - && path.ident.name == sym!(split_whitespace) + && path.ident.name.as_str() == "split_whitespace" && let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id) && cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id) && let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index be32dc0aab0..0d809c17989 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -334,7 +334,7 @@ fn strip_non_ident_wrappers(expr: &Expr) -> &Expr { let mut output = expr; loop { output = match &output.kind { - ExprKind::Paren(ref inner) | ExprKind::Unary(_, ref inner) => inner, + ExprKind::Paren(inner) | ExprKind::Unary(_, inner) => inner, _ => { return output; }, @@ -348,13 +348,13 @@ fn extract_related_binops(kind: &ExprKind) -> Option>> { fn if_statement_binops(kind: &ExprKind) -> Option>> { match kind { - ExprKind::If(ref condition, _, _) => chained_binops(&condition.kind), - ExprKind::Paren(ref e) => if_statement_binops(&e.kind), - ExprKind::Block(ref block, _) => { + ExprKind::If(condition, _, _) => chained_binops(&condition.kind), + ExprKind::Paren(e) => if_statement_binops(&e.kind), + ExprKind::Block(block, _) => { let mut output = None; for stmt in &block.stmts { - match stmt.kind { - StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => { + match &stmt.kind { + StmtKind::Expr(e) | StmtKind::Semi(e) => { output = append_opt_vecs(output, if_statement_binops(&e.kind)); }, _ => {}, @@ -383,7 +383,7 @@ fn append_opt_vecs(target_opt: Option>, source_opt: Option>) -> fn chained_binops(kind: &ExprKind) -> Option>> { match kind { ExprKind::Binary(_, left_outer, right_outer) => chained_binops_helper(left_outer, right_outer), - ExprKind::Paren(ref e) | ExprKind::Unary(_, ref e) => chained_binops(&e.kind), + ExprKind::Paren(e) | ExprKind::Unary(_, e) => chained_binops(&e.kind), _ => None, } } @@ -391,16 +391,14 @@ fn chained_binops(kind: &ExprKind) -> Option>> { fn chained_binops_helper<'expr>(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option>> { match (&left_outer.kind, &right_outer.kind) { ( - ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), - ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e), + ExprKind::Paren(left_e) | ExprKind::Unary(_, left_e), + ExprKind::Paren(right_e) | ExprKind::Unary(_, right_e), ) => chained_binops_helper(left_e, right_e), - (ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), _) => chained_binops_helper(left_e, right_outer), - (_, ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e)) => { - chained_binops_helper(left_outer, right_e) - }, + (ExprKind::Paren(left_e) | ExprKind::Unary(_, left_e), _) => chained_binops_helper(left_e, right_outer), + (_, ExprKind::Paren(right_e) | ExprKind::Unary(_, right_e)) => chained_binops_helper(left_outer, right_e), ( - ExprKind::Binary(Spanned { node: left_op, .. }, ref left_left, ref left_right), - ExprKind::Binary(Spanned { node: right_op, .. }, ref right_left, ref right_right), + ExprKind::Binary(Spanned { node: left_op, .. }, left_left, left_right), + ExprKind::Binary(Spanned { node: right_op, .. }, right_left, right_right), ) => match ( chained_binops_helper(left_left, left_right), chained_binops_helper(right_left, right_right), diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 6cba560393d..23362506cca 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -392,7 +392,7 @@ fn is_used_after_swap(&mut self, idx_ident: Ident) -> bool { for stmt in self.block.stmts { match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => v.visit_expr(expr), - StmtKind::Let(LetStmt { ref init, .. }) => { + StmtKind::Let(LetStmt { init, .. }) => { if let Some(init) = init.as_ref() { v.visit_expr(init); } diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 3da4bf67558..07bf4319ff0 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -10,8 +10,8 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{ - GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath, - TraitBoundModifiers, TraitItem, TraitRef, Ty, TyKind, WherePredicate, BoundPolarity, + BoundPolarity, GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath, + TraitBoundModifiers, TraitItem, TraitRef, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -124,7 +124,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tc let mut self_bounds_map = FxHashMap::default(); for predicate in item.generics.predicates { - if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate + if let WherePredicate::BoundPredicate(bound_predicate) = predicate && bound_predicate.origin != PredicateOrigin::ImplTrait && !bound_predicate.span.from_expansion() && let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind @@ -268,7 +268,7 @@ impl Eq for SpanlessTy<'_, '_> {} let mut map: UnhashMap, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in generics.predicates { - if let WherePredicate::BoundPredicate(ref p) = bound + if let WherePredicate::BoundPredicate(p) = bound && p.origin != PredicateOrigin::ImplTrait && p.bounds.len() as u64 <= self.max_trait_bounds && !p.span.from_expansion() @@ -379,7 +379,7 @@ struct ComparableTraitRef<'a, 'tcx> { impl PartialEq for ComparableTraitRef<'_, '_> { fn eq(&self, other: &Self) -> bool { - SpanlessEq::new(self.cx).eq_modifiers(self.modifiers, other.modifiers) + SpanlessEq::eq_modifiers(self.modifiers, other.modifiers) && SpanlessEq::new(self.cx) .paths_by_resolution() .eq_path(self.trait_ref.path, other.trait_ref.path) diff --git a/clippy_lints/src/transmute/eager_transmute.rs b/clippy_lints/src/transmute/eager_transmute.rs index 1dfc9f7091e..ca9daf2d2a0 100644 --- a/clippy_lints/src/transmute/eager_transmute.rs +++ b/clippy_lints/src/transmute/eager_transmute.rs @@ -43,7 +43,7 @@ fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_ binops_with_local(cx, local_expr, lhs) || binops_with_local(cx, local_expr, rhs) }, ExprKind::MethodCall(path, receiver, [arg], _) - if path.ident.name == sym!(contains) + if path.ident.name.as_str() == "contains" // ... `contains` called on some kind of range && let Some(receiver_adt) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def() && let lang_items = cx.tcx.lang_items() @@ -81,7 +81,7 @@ pub(super) fn check<'tcx>( if let Some(then_some_call) = peel_parent_unsafe_blocks(cx, expr) && let ExprKind::MethodCall(path, receiver, [arg], _) = then_some_call.kind && cx.typeck_results().expr_ty(receiver).is_bool() - && path.ident.name == sym!(then_some) + && path.ident.name.as_str() == "then_some" && is_local_with_projections(transmutable) && binops_with_local(cx, transmutable, receiver) && is_normalizable(cx, cx.param_env, from_ty) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 44eb0a6c593..0bba611116b 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -295,7 +295,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>( if cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, ref node)| { matches!( node, - Node::Block(&Block { + Node::Block(Block { rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), .. }), diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index bf2d75ea1a9..1a1284ce9c4 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -194,7 +194,7 @@ fn needs_inferred_result_ty( let (id, receiver, args) = match e.kind { ExprKind::Call( Expr { - kind: ExprKind::Path(ref path), + kind: ExprKind::Path(path), hir_id, .. }, diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index e7d26fa238e..0f4bd286145 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -149,7 +149,7 @@ fn is_empty_block(expr: &Expr<'_>) -> bool { expr.kind, ExprKind::Block( Block { - stmts: &[], + stmts: [], expr: None, .. }, diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index cf406b817da..7c9455bf8ab 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths, peel_blocks}; +use clippy_utils::{is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, paths, peel_blocks}; use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -83,6 +83,28 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { /// to consider the arms, and we want to avoid breaking the logic for situations where things /// get desugared to match. fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) { + let fn_def_id = block.hir_id.owner.to_def_id(); + if let Some(impl_id) = cx.tcx.impl_of_method(fn_def_id) + && let Some(trait_id) = cx.tcx.trait_id_of_impl(impl_id) + { + // We don't want to lint inside io::Read or io::Write implementations, as the author has more + // information about their trait implementation than our lint, see https://github.com/rust-lang/rust-clippy/issues/4836 + if cx.tcx.is_diagnostic_item(sym::IoRead, trait_id) || cx.tcx.is_diagnostic_item(sym::IoWrite, trait_id) { + return; + } + + let async_paths: [&[&str]; 4] = [ + &paths::TOKIO_IO_ASYNCREADEXT, + &paths::TOKIO_IO_ASYNCWRITEEXT, + &paths::FUTURES_IO_ASYNCREADEXT, + &paths::FUTURES_IO_ASYNCWRITEEXT, + ]; + + if async_paths.into_iter().any(|path| match_def_path(cx, trait_id, path)) { + return; + } + } + for stmt in block.stmts { if let hir::StmtKind::Semi(exp) = stmt.kind { check_expr(cx, exp); @@ -222,7 +244,7 @@ fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { } fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { - while let ExprKind::Call(func, [ref arg_0]) = expr.kind + while let ExprKind::Call(func, [arg_0]) = expr.kind && matches!( func.kind, ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..)) @@ -244,7 +266,7 @@ fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { /// waited on. Otherwise return None. fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { - if let ExprKind::Call(func, [ref arg_0]) = expr.kind { + if let ExprKind::Call(func, [arg_0]) = expr.kind { if matches!( func.kind, ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 31f9d84f5e4..1bf24083665 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -396,7 +396,7 @@ macro_rules! kind { self.pat(field!(let_expr.pat)); // Does what ExprKind::Cast does, only adds a clause for the type // if it's a path - if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { + if let Some(TyKind::Path(qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { bind!(self, qpath); chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); self.qpath(qpath); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index f662c7651f6..deb983b6971 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -6,5 +6,6 @@ pub mod msrv_attr_impl; pub mod outer_expn_data_pass; pub mod produce_ice; +pub mod slow_symbol_comparisons; pub mod unnecessary_def_path; pub mod unsorted_clippy_utils_paths; diff --git a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs index a3f9abe4f96..eaeb754a23f 100644 --- a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs +++ b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -79,7 +79,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Call(func, [call_cx, call_lint, call_sp, call_msg, call_f]) = expr.kind && is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]) - && let ExprKind::Closure(&Closure { body, .. }) = &call_f.kind + && let ExprKind::Closure(&Closure { body, .. }) = call_f.kind && let body = cx.tcx.hir().body(body) && let only_expr = peel_blocks_with_stmt(body.value) && let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind 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 51235de9f29..ed119aa46ca 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 @@ -123,7 +123,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { .expect("lints must have a description field"); if let ExprKind::Lit(Spanned { - node: LitKind::Str(ref sym, _), + node: LitKind::Str(sym, _), .. }) = field.expr.kind { @@ -218,9 +218,7 @@ pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { if let Some(value) = extract_clippy_version_value(cx, item) { - // The `sym!` macro doesn't work as it only expects a single token. - // It's better to keep it this way and have a direct `Symbol::intern` call here. - if value == Symbol::intern("pre 1.29.0") { + if value.as_str() == "pre 1.29.0" { return; } @@ -251,7 +249,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<' pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { let attrs = cx.tcx.hir().attrs(item.hir_id()); attrs.iter().find_map(|attr| { - if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind + if let ast::AttrKind::Normal(attr_kind) = &attr.kind // Identify attribute && let [tool_name, attr_name] = &attr_kind.item.path.segments[..] && tool_name.ident.name == sym::clippy diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs index 63fcbd61528..68692246153 100644 --- a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -42,7 +42,7 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV)) }) - && !items.iter().any(|item| item.ident.name == sym!(check_attributes)) + && !items.iter().any(|item| item.ident.name.as_str() == "check_attributes") { let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; diff --git a/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs b/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs new file mode 100644 index 00000000000..3742be0e103 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs @@ -0,0 +1,69 @@ +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::match_type; +use clippy_utils::{match_function_call, paths}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// + /// Detects symbol comparision using `Symbol::intern`. + /// + /// ### Why is this bad? + /// + /// Comparision via `Symbol::as_str()` is faster if the interned symbols are not reused. + /// + /// ### Example + /// + /// None, see suggestion. + pub SLOW_SYMBOL_COMPARISONS, + internal, + "detects slow comparisions of symbol" +} + +declare_lint_pass!(SlowSymbolComparisons => [SLOW_SYMBOL_COMPARISONS]); + +fn check_slow_comparison<'tcx>( + cx: &LateContext<'tcx>, + op1: &'tcx Expr<'tcx>, + op2: &'tcx Expr<'tcx>, +) -> Option<(Span, String)> { + if match_type(cx, cx.typeck_results().expr_ty(op1), &paths::SYMBOL) + && let Some([symbol_name_expr]) = match_function_call(cx, op2, &paths::SYMBOL_INTERN) + && let Some(Constant::Str(symbol_name)) = ConstEvalCtxt::new(cx).eval_simple(symbol_name_expr) + { + Some((op1.span, symbol_name)) + } else { + None + } +} + +impl<'tcx> LateLintPass<'tcx> for SlowSymbolComparisons { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::Binary(op, left, right) = expr.kind + && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) + && let Some((symbol_span, symbol_name)) = + check_slow_comparison(cx, left, right).or_else(|| check_slow_comparison(cx, right, left)) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + SLOW_SYMBOL_COMPARISONS, + expr.span, + "comparing `Symbol` via `Symbol::intern`", + "use `Symbol::as_str` and check the string instead", + format!( + "{}.as_str() {} \"{symbol_name}\"", + snippet_with_applicability(cx, symbol_span, "symbol", &mut applicability), + op.node.as_str() + ), + applicability, + ); + }; + } +} diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index c5e2c8c09a2..75169e05734 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -21,7 +21,7 @@ ImplItem, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource, }; -use rustc_lint::{LateContext, LintContext, EarlyContext}; +use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::symbol::{Ident, kw}; @@ -63,8 +63,8 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit), } && match end_pat { Pat::Str(text) => end_str.ends_with(text), - Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)), - Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)), + Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)), + Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)), Pat::Sym(sym) => end_str.ends_with(sym.as_str()), Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit), }) @@ -333,26 +333,32 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) { match attr.kind { AttrKind::Normal(..) => { if let Some(ident) = attr.ident() { - // TODO: I feel like it's likely we can use `Cow` instead but this will require quite a bit of - // refactoring // NOTE: This will likely have false positives, like `allow = 1` - ( - Pat::OwnedMultiStr(vec![ident.to_string(), "#".to_owned()]), - Pat::Str(""), - ) + let ident_string = ident.to_string(); + if attr.style == AttrStyle::Outer { + ( + Pat::OwnedMultiStr(vec!["#[".to_owned() + &ident_string, ident_string]), + Pat::Str(""), + ) + } else { + ( + Pat::OwnedMultiStr(vec!["#![".to_owned() + &ident_string, ident_string]), + Pat::Str(""), + ) + } } else { (Pat::Str("#"), Pat::Str("]")) } }, AttrKind::DocComment(_kind @ CommentKind::Line, ..) => { - if matches!(attr.style, AttrStyle::Outer) { + if attr.style == AttrStyle::Outer { (Pat::Str("///"), Pat::Str("")) } else { (Pat::Str("//!"), Pat::Str("")) } }, AttrKind::DocComment(_kind @ CommentKind::Block, ..) => { - if matches!(attr.style, AttrStyle::Outer) { + if attr.style == AttrStyle::Outer { (Pat::Str("/**"), Pat::Str("*/")) } else { (Pat::Str("/*!"), Pat::Str("*/")) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 67c31abbdda..24a02c7ef87 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -1,3 +1,7 @@ +//! A simple const eval API, for use on arbitrary HIR expressions. +//! +//! This cannot use rustc's const eval, aka miri, as arbitrary HIR expressions cannot be lowered to +//! executable MIR bodies, so we have to do this instead. #![allow(clippy::float_cmp)] use crate::macros::HirNode; @@ -379,6 +383,8 @@ fn cmp_s_u(s: i128, u: u128) -> Ordering { /// The context required to evaluate a constant expression. /// /// This is currently limited to constant folding and reading the value of named constants. +/// +/// See the module level documentation for some context. pub struct ConstEvalCtxt<'tcx> { tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 4877fb65d37..993035001c1 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -8,7 +8,9 @@ //! Thank you! //! ~The `INTERNAL_METADATA_COLLECTOR` lint -use rustc_errors::{Applicability, Diag, DiagMessage, MultiSpan, SubdiagMessage}; +use rustc_errors::{ + Applicability, Diag, DiagMessage, EmissionGuarantee, MultiSpan, SubdiagMessage, SubstitutionPart, Suggestions, +}; use rustc_hir::HirId; use rustc_lint::{LateContext, Lint, LintContext}; use rustc_span::Span; @@ -28,6 +30,42 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) { } } +/// Makes sure that a diagnostic is well formed. +/// +/// rustc debug asserts a few properties about spans, +/// but the clippy repo uses a distributed rustc build with debug assertions disabled, +/// so this has historically led to problems during subtree syncs where those debug assertions +/// only started triggered there. +/// +/// This function makes sure we also validate them in debug clippy builds. +fn validate_diag(diag: &Diag<'_, impl EmissionGuarantee>) { + let suggestions = match &diag.suggestions { + Suggestions::Enabled(suggs) => &**suggs, + Suggestions::Sealed(suggs) => &**suggs, + Suggestions::Disabled => return, + }; + + for substitution in suggestions.iter().flat_map(|s| &s.substitutions) { + assert_eq!( + substitution + .parts + .iter() + .find(|SubstitutionPart { snippet, span }| snippet.is_empty() && span.is_empty()), + None, + "span must not be empty and have no suggestion" + ); + + assert_eq!( + substitution + .parts + .array_windows() + .find(|[a, b]| a.span.overlaps(b.span)), + None, + "suggestion must not have overlapping parts" + ); + } +} + /// Emit a basic lint message with a `msg` and a `span`. /// /// This is the most primitive of our lint emission methods and can @@ -64,6 +102,9 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( diag.help(help.into()); } docs_link(diag, lint); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } @@ -175,6 +219,9 @@ pub fn span_lint_and_note( diag.note(note.into()); } docs_link(diag, lint); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } @@ -208,6 +255,9 @@ pub fn span_lint_and_then(cx: &C, lint: &'static Lint, sp: S, msg: M diag.primary_message(msg); f(diag); docs_link(diag, lint); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } @@ -240,6 +290,9 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s cx.tcx.node_span_lint(lint, hir_id, sp, |diag| { diag.primary_message(msg); docs_link(diag, lint); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } @@ -280,6 +333,9 @@ pub fn span_lint_hir_and_then( diag.primary_message(msg); f(diag); docs_link(diag, lint); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } @@ -316,7 +372,7 @@ pub fn span_lint_hir_and_then( /// | /// = note: `-D fold-any` implied by `-D warnings` /// ``` -#[expect(clippy::collapsible_span_lint_calls)] +#[cfg_attr(not(debug_assertions), expect(clippy::collapsible_span_lint_calls))] pub fn span_lint_and_sugg( cx: &T, lint: &'static Lint, @@ -328,5 +384,8 @@ pub fn span_lint_and_sugg( ) { span_lint_and_then(cx, lint, sp, msg.into(), |diag| { diag.span_suggestion(sp, help.into(), sugg, applicability); + + #[cfg(debug_assertions)] + validate_diag(diag); }); } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 3175a9a1dd3..11bbe734844 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -103,7 +103,7 @@ impl<'hir> IfLet<'hir> { /// Parses an `if let` expression pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option { if let ExprKind::If( - Expr { + &Expr { kind: ExprKind::Let(&hir::LetExpr { pat: let_pat, @@ -381,12 +381,12 @@ impl<'hir> WhileLet<'hir> { /// Parses a desugared `while let` loop pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::Loop( - Block { + &Block { expr: - Some(Expr { + Some(&Expr { kind: ExprKind::If( - Expr { + &Expr { kind: ExprKind::Let(&hir::LetExpr { pat: let_pat, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 8004bc68b2e..cb69f8e5a0e 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -128,7 +128,7 @@ pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegmen self.inter_expr().eq_path_segments(left, right) } - pub fn eq_modifiers(&mut self, left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool { + pub fn eq_modifiers(left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool { std::mem::discriminant(&left.constness) == std::mem::discriminant(&right.constness) && std::mem::discriminant(&left.polarity) == std::mem::discriminant(&right.polarity) } @@ -1201,11 +1201,11 @@ pub fn hash_tykind(&mut self, ty: &TyKind<'_>) { self.hash_ty(ty); self.hash_pat(pat); }, - TyKind::Ptr(ref mut_ty) => { + TyKind::Ptr(mut_ty) => { self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); }, - TyKind::Ref(lifetime, ref mut_ty) => { + TyKind::Ref(lifetime, mut_ty) => { self.hash_lifetime(lifetime); self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); @@ -1230,14 +1230,19 @@ pub fn hash_tykind(&mut self, ty: &TyKind<'_>) { self.hash_ty(ty); } }, - TyKind::Path(ref qpath) => self.hash_qpath(qpath), + TyKind::Path(qpath) => self.hash_qpath(qpath), TyKind::TraitObject(_, lifetime, _) => { self.hash_lifetime(lifetime); }, TyKind::Typeof(anon_const) => { self.hash_body(anon_const.body); }, - TyKind::Err(_) | TyKind::Infer | TyKind::Never | TyKind::InferDelegation(..) | TyKind::OpaqueDef(_) | TyKind::AnonAdt(_) => {}, + TyKind::Err(_) + | TyKind::Infer + | TyKind::Never + | TyKind::InferDelegation(..) + | TyKind::OpaqueDef(_) + | TyKind::AnonAdt(_) => {}, } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0f712068e65..50d334acf18 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -9,6 +9,7 @@ #![feature(rustc_private)] #![feature(assert_matches)] #![feature(unwrap_infallible)] +#![feature(array_windows)] #![recursion_limit = "512"] #![allow( clippy::missing_errors_doc, @@ -688,11 +689,11 @@ pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec { /// /// This function is expensive and should be used sparingly. pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec { - let (base, path) = match *path { + let (base, path) = match path { [primitive] => { return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; }, - [base, ref path @ ..] => (base, path), + [base, path @ ..] => (base, path), _ => return Vec::new(), }; @@ -744,7 +745,7 @@ pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec, mut path: &[& } /// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`]. -pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator { +pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator + use<> { def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id()) } @@ -934,7 +935,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { } }, ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func), - ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg), + ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg), ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), _ => false, @@ -947,7 +948,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: & { match arg.kind { ExprKind::Lit(hir::Lit { - node: LitKind::Str(ref sym, _), + node: LitKind::Str(sym, _), .. }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String), ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), @@ -1337,15 +1338,17 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { pub struct ContainsName<'a, 'tcx> { pub cx: &'a LateContext<'tcx>, pub name: Symbol, - pub result: bool, } impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> { + type Result = ControlFlow<()>; type NestedFilter = nested_filter::OnlyBodies; - fn visit_name(&mut self, name: Symbol) { + fn visit_name(&mut self, name: Symbol) -> Self::Result { if self.name == name { - self.result = true; + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } } @@ -1356,13 +1359,8 @@ fn nested_visit_map(&mut self) -> Self::Map { /// Checks if an `Expr` contains a certain name. pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool { - let mut cn = ContainsName { - name, - result: false, - cx, - }; - cn.visit_expr(expr); - cn.result + let mut cn = ContainsName { cx, name }; + cn.visit_expr(expr).is_break() } /// Returns `true` if `expr` contains a return expression @@ -3459,7 +3457,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool { matches!( cx.tcx.parent_hir_node(id), - Node::Stmt(..) | Node::Block(Block { stmts: &[], .. }) + Node::Stmt(..) | Node::Block(Block { stmts: [], .. }) ) } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 41785e161d0..ce1a20e0066 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -19,7 +19,7 @@ use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, TypingMode, + TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; @@ -274,11 +274,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>( .map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into())) .collect::>(); - let trait_ref = TraitRef::new( - tcx, - trait_id, - [GenericArg::from(ty)].into_iter().chain(args), - ); + let trait_ref = TraitRef::new(tcx, trait_id, [GenericArg::from(ty)].into_iter().chain(args)); debug_assert_matches!( tcx.def_kind(trait_id), @@ -975,9 +971,7 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) { (Ok(size), _) => size, (Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(), - (Err(_), ty::Array(t, n)) => { - n.try_to_target_usize(cx.tcx).unwrap_or_default() * approx_ty_size(cx, *t) - }, + (Err(_), ty::Array(t, n)) => n.try_to_target_usize(cx.tcx).unwrap_or_default() * approx_ty_size(cx, *t), (Err(_), ty::Adt(def, subst)) if def.is_struct() => def .variants() .iter() @@ -1284,7 +1278,12 @@ fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { let cause = ObligationCause::dummy(); - match tcx.infer_ctxt().build(TypingMode::from_param_env(param_env)).at(&cause, param_env).query_normalize(ty) { + match tcx + .infer_ctxt() + .build(TypingMode::from_param_env(param_env)) + .at(&cause, param_env) + .query_normalize(ty) + { Ok(ty) => ty.value, Err(_) => ty, } diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 3021f21df12..d7640ebfb00 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -302,9 +302,9 @@ fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bo // Check that all type parameters appear in the functions input types. (0..(generics.parent_count + generics.own_params.len()) as u32).all(|index| { fn_sig - .inputs() - .iter() - .any(|input_ty| contains_param(*input_ty.skip_binder(), index)) + .inputs() + .iter() + .any(|input_ty| contains_param(*input_ty.skip_binder(), index)) }) } diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 4bfc9dd9213..c8c25456f69 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -109,34 +109,28 @@ fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { pub struct BindingUsageFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, binding_ids: Vec, - usage_found: bool, } impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> { pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool { let mut finder = BindingUsageFinder { cx, binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body), - usage_found: false, }; - finder.visit_body(body); - finder.usage_found + finder.visit_body(body).is_break() } } impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> { + type Result = ControlFlow<()>; type NestedFilter = nested_filter::OnlyBodies; - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if !self.usage_found { - intravisit::walk_expr(self, expr); - } - } - - fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) { + fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) -> Self::Result { if let Res::Local(id) = path.res { if self.binding_ids.contains(&id) { - self.usage_found = true; + return ControlFlow::Break(()); } } + + ControlFlow::Continue(()) } fn nested_visit_map(&mut self) -> Self::Map { diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 8db6502dbfb..8f5ec185bf1 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -324,17 +324,15 @@ pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tc pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, - is_const: bool, } + impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + type Result = ControlFlow<()>; type NestedFilter = intravisit::nested_filter::None; - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if !self.is_const { - return; - } + fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { match e.kind { - ExprKind::ConstBlock(_) => return, + ExprKind::ConstBlock(_) => return ControlFlow::Continue(()), ExprKind::Call( &Expr { kind: ExprKind::Path(ref p), @@ -394,37 +392,34 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) { | ExprKind::Type(..) => (), _ => { - self.is_const = false; - return; + return ControlFlow::Break(()); }, } - walk_expr(self, e); + + walk_expr(self, e) } } - let mut v = V { cx, is_const: true }; - v.visit_expr(e); - v.is_const + let mut v = V { cx }; + v.visit_expr(e).is_continue() } /// Checks if the given expression performs an unsafe operation outside of an unsafe block. pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, - is_unsafe: bool, } impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; + type Result = ControlFlow<()>; + fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if self.is_unsafe { - return; - } + fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { match e.kind { ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => { - self.is_unsafe = true; + ControlFlow::Break(()) }, ExprKind::MethodCall(..) if self @@ -435,13 +430,13 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) { self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe }) => { - self.is_unsafe = true; + ControlFlow::Break(()) }, ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() { ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe => { - self.is_unsafe = true; + ControlFlow::Break(()) }, - ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => self.is_unsafe = true, + ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => ControlFlow::Break(()), _ => walk_expr(self, e), }, ExprKind::Path(ref p) @@ -451,56 +446,54 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) { .opt_def_id() .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) => { - self.is_unsafe = true; + ControlFlow::Break(()) }, _ => walk_expr(self, e), } } - fn visit_block(&mut self, b: &'tcx Block<'_>) { - if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) { - walk_block(self, b); + fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result { + if matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) { + ControlFlow::Continue(()) + } else { + walk_block(self, b) } } - fn visit_nested_item(&mut self, id: ItemId) { - if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind { - self.is_unsafe = i.safety == Safety::Unsafe; + fn visit_nested_item(&mut self, id: ItemId) -> Self::Result { + if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind + && i.safety == Safety::Unsafe + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } } } - let mut v = V { cx, is_unsafe: false }; - v.visit_expr(e); - v.is_unsafe + let mut v = V { cx }; + v.visit_expr(e).is_break() } /// Checks if the given expression contains an unsafe block pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, - found_unsafe: bool, } impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + type Result = ControlFlow<()>; type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } - fn visit_block(&mut self, b: &'tcx Block<'_>) { - if self.found_unsafe { - return; - } + fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result { if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { - self.found_unsafe = true; - return; + ControlFlow::Break(()) + } else { + walk_block(self, b) } - walk_block(self, b); } } - let mut v = V { - cx, - found_unsafe: false, - }; - v.visit_expr(e); - v.found_unsafe + let mut v = V { cx }; + v.visit_expr(e).is_break() } /// Runs the given function for each sub-expression producing the final value consumed by the parent diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 5774e20e0be..b8e0413e97b 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -396,7 +396,8 @@ struct Renderer<'a> { impl Renderer<'_> { fn markdown(input: &str) -> Safe { - let parser = Parser::new_ext(input, Options::all()); + let input = clippy_config::sanitize_explanation(input); + let parser = Parser::new_ext(&input, Options::all()); let mut html_output = String::new(); html::push_html(&mut html_output, parser); // Oh deer, what a hack :O diff --git a/tests/ui-internal/slow_symbol_comparisons.fixed b/tests/ui-internal/slow_symbol_comparisons.fixed new file mode 100644 index 00000000000..2cbd646a0fd --- /dev/null +++ b/tests/ui-internal/slow_symbol_comparisons.fixed @@ -0,0 +1,24 @@ +#![feature(rustc_private)] +#![warn(clippy::slow_symbol_comparisons)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::Symbol; + +fn main() { + let symbol = sym!(example); + let other_symbol = sym!(other_example); + + // Should lint + let slow_comparison = symbol.as_str() == "example"; + //~^ error: comparing `Symbol` via `Symbol::intern` + let slow_comparison_macro = symbol.as_str() == "example"; + //~^ error: comparing `Symbol` via `Symbol::intern` + let slow_comparison_backwards = symbol.as_str() == "example"; + //~^ error: comparing `Symbol` via `Symbol::intern` + + // Should not lint + let faster_comparison = symbol.as_str() == "other_example"; + let preinterned_comparison = symbol == other_symbol; +} diff --git a/tests/ui-internal/slow_symbol_comparisons.rs b/tests/ui-internal/slow_symbol_comparisons.rs new file mode 100644 index 00000000000..0cea3c3fcff --- /dev/null +++ b/tests/ui-internal/slow_symbol_comparisons.rs @@ -0,0 +1,24 @@ +#![feature(rustc_private)] +#![warn(clippy::slow_symbol_comparisons)] + +extern crate rustc_span; + +use clippy_utils::sym; +use rustc_span::Symbol; + +fn main() { + let symbol = sym!(example); + let other_symbol = sym!(other_example); + + // Should lint + let slow_comparison = symbol == Symbol::intern("example"); + //~^ error: comparing `Symbol` via `Symbol::intern` + let slow_comparison_macro = symbol == sym!(example); + //~^ error: comparing `Symbol` via `Symbol::intern` + let slow_comparison_backwards = sym!(example) == symbol; + //~^ error: comparing `Symbol` via `Symbol::intern` + + // Should not lint + let faster_comparison = symbol.as_str() == "other_example"; + let preinterned_comparison = symbol == other_symbol; +} diff --git a/tests/ui-internal/slow_symbol_comparisons.stderr b/tests/ui-internal/slow_symbol_comparisons.stderr new file mode 100644 index 00000000000..72cb20a7fed --- /dev/null +++ b/tests/ui-internal/slow_symbol_comparisons.stderr @@ -0,0 +1,23 @@ +error: comparing `Symbol` via `Symbol::intern` + --> tests/ui-internal/slow_symbol_comparisons.rs:14:27 + | +LL | let slow_comparison = symbol == Symbol::intern("example"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"` + | + = note: `-D clippy::slow-symbol-comparisons` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::slow_symbol_comparisons)]` + +error: comparing `Symbol` via `Symbol::intern` + --> tests/ui-internal/slow_symbol_comparisons.rs:16:33 + | +LL | let slow_comparison_macro = symbol == sym!(example); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"` + +error: comparing `Symbol` via `Symbol::intern` + --> tests/ui-internal/slow_symbol_comparisons.rs:18:37 + | +LL | let slow_comparison_backwards = sym!(example) == symbol; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"` + +error: aborting due to 3 previous errors + diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index 9d7c00088fe..e4575d99d03 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -19,8 +19,8 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"] error: hardcoded path to a diagnostic item --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:43 | -LL | const OPS_MOD: [&str; 5] = ["core", "ops"]; - | ^^^^^^^^^^^^^^^ +LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: convert all references to use `sym::deref_method` diff --git a/tests/ui-internal/unnecessary_symbol_str.fixed b/tests/ui-internal/unnecessary_symbol_str.fixed index eb79fdbc4b4..8e7f020c1f6 100644 --- a/tests/ui-internal/unnecessary_symbol_str.fixed +++ b/tests/ui-internal/unnecessary_symbol_str.fixed @@ -1,6 +1,7 @@ #![feature(rustc_private)] #![deny(clippy::internal)] #![allow( + clippy::slow_symbol_comparisons, clippy::borrow_deref_ref, clippy::unnecessary_operation, unused_must_use, diff --git a/tests/ui-internal/unnecessary_symbol_str.rs b/tests/ui-internal/unnecessary_symbol_str.rs index bbea13af92a..9aeeb9aaf3a 100644 --- a/tests/ui-internal/unnecessary_symbol_str.rs +++ b/tests/ui-internal/unnecessary_symbol_str.rs @@ -1,6 +1,7 @@ #![feature(rustc_private)] #![deny(clippy::internal)] #![allow( + clippy::slow_symbol_comparisons, clippy::borrow_deref_ref, clippy::unnecessary_operation, unused_must_use, diff --git a/tests/ui-internal/unnecessary_symbol_str.stderr b/tests/ui-internal/unnecessary_symbol_str.stderr index 551167a9ff5..668c11722f9 100644 --- a/tests/ui-internal/unnecessary_symbol_str.stderr +++ b/tests/ui-internal/unnecessary_symbol_str.stderr @@ -1,5 +1,5 @@ error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:15:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:16:5 | LL | Symbol::intern("foo").as_str() == "clippy"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy` @@ -12,25 +12,25 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:16:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:17:5 | LL | Symbol::intern("foo").to_string() == "self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:17:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:18:5 | LL | Symbol::intern("foo").to_ident_string() != "Self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:18:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:19:5 | LL | &*Ident::empty().as_str() == "clippy"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` error: unnecessary `Symbol` to string conversion - --> tests/ui-internal/unnecessary_symbol_str.rs:19:5 + --> tests/ui-internal/unnecessary_symbol_str.rs:20:5 | LL | "clippy" == Ident::empty().to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` diff --git a/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml new file mode 100644 index 00000000000..fbdb0c9c37d --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml @@ -0,0 +1 @@ +trait-assoc-item-kinds-order = ["fn", "type", "const", "type"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml new file mode 100644 index 00000000000..720655e5cce --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml @@ -0,0 +1 @@ +trait-assoc-item-kinds-order = ["const", "type"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml new file mode 100644 index 00000000000..0dd5407be10 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml @@ -0,0 +1 @@ +source-item-ordering = ["enum", "impl", "module", "struct", "trait", "struct"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/default/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/default/clippy.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml new file mode 100644 index 00000000000..ddca5cfa577 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml @@ -0,0 +1,12 @@ +trait-assoc-item-kinds-order = ["const", "type", "fn"] +source-item-ordering = ["enum", "impl", "module", "struct", "trait"] +module-item-order-groupings = [ + ["modules", ["extern_crate", "mod", "foreign_mod"]], + ["use", ["use"]], + ["macros", ["macro"]], + ["global_asm", ["global_asm"]], + ["UPPER_SNAKE_CASE", ["static", "const"]], + ["PascalCase", ["ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl"]], + ["lower_snake_case", ["fn"]] +] + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/only_enum/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/only_enum/clippy.toml new file mode 100644 index 00000000000..2144bdc9a0c --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/only_enum/clippy.toml @@ -0,0 +1 @@ +source-item-ordering = ["enum"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/only_impl/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/only_impl/clippy.toml new file mode 100644 index 00000000000..54b6727fabf --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/only_impl/clippy.toml @@ -0,0 +1 @@ +source-item-ordering = ["impl"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/only_trait/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/only_trait/clippy.toml new file mode 100644 index 00000000000..b551611c35e --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/only_trait/clippy.toml @@ -0,0 +1 @@ +source-item-ordering = ["trait"] diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_1.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_1.stderr new file mode 100644 index 00000000000..e441c7c1241 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_1.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. The trait associated item kinds are: [Const, Fn, Type] + --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml:1:32 + | +LL | trait-assoc-item-kinds-order = ["fn", "type", "const", "type"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr new file mode 100644 index 00000000000..183f0b03319 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. The trait associated item kinds are: [Const, Fn, Type] + --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml:1:32 + | +LL | trait-assoc-item-kinds-order = ["const", "type"] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr new file mode 100644 index 00000000000..abf58dbd110 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr @@ -0,0 +1,8 @@ +error: error reading Clippy's configuration file: The category "Struct" was enabled more than once in the source ordering configuration. + --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml:1:24 + | +LL | source-item-ordering = ["enum", "impl", "module", "struct", "trait", "struct"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs new file mode 100644 index 00000000000..e9bcc30c15e --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs @@ -0,0 +1,186 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: default default_exp bad_conf_1 bad_conf_2 bad_conf_3 +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default +//@[default_exp] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default_exp +//@[bad_conf_1] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1 +//@[bad_conf_2] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2 +//@[bad_conf_3] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3 + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +/// This module gets linted before clippy gives up. +mod i_am_just_right { + const AFTER: i8 = 0; + + const BEFORE: i8 = 0; +} + +/// Since the upper module passes linting, the lint now recurses into this module. +mod this_is_in_the_wrong_position { + const A: i8 = 1; + const C: i8 = 0; +} + +// Use statements should not be linted internally - this is normally auto-sorted using rustfmt. +use std::rc::{Rc, Weak}; +use std::sync::{Arc, Barrier, RwLock}; + +const SNAKE_CASE: &str = "zzzzzzzz"; + +const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + +const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + +trait BasicEmptyTrait {} + +trait CloneSelf { + fn clone_self(&self) -> Self; +} + +enum EnumOrdered { + A, + B, + C, +} + +enum EnumUnordered { + A, + B, + C, +} + +#[allow(clippy::arbitrary_source_item_ordering)] +enum EnumUnorderedAllowed { + A, + B, + C, +} + +struct StructOrdered { + a: bool, + b: bool, + c: bool, +} + +impl BasicEmptyTrait for StructOrdered {} + +impl CloneSelf for StructOrdered { + fn clone_self(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl Default for StructOrdered { + fn default() -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl std::clone::Clone for StructOrdered { + fn clone(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +#[derive(Clone, Default)] +struct StructUnordered { + a: bool, + b: bool, + c: bool, + d: bool, +} + +impl TraitUnordered for StructUnordered { + const A: bool = false; + const B: bool = false; + const C: bool = false; + + type SomeType = (); + + fn a() {} + fn b() {} + fn c() {} +} + +impl TraitUnorderedItemKinds for StructUnordered { + const A: bool = false; + const B: bool = false; + const C: bool = false; + + type SomeType = (); + + fn a() {} + fn b() {} + fn c() {} +} + +struct StructUnorderedGeneric { + _1: std::marker::PhantomData, + a: bool, + b: bool, + c: bool, + d: bool, +} + +trait TraitOrdered { + const A: bool; + const B: bool; + const C: bool; + + type SomeType; + + fn a(); + fn b(); + fn c(); +} + +trait TraitUnordered { + const A: bool; + const B: bool; + const C: bool; + + type SomeType; + + fn a(); + fn b(); + fn c(); +} + +trait TraitUnorderedItemKinds { + const A: bool; + const B: bool; + const C: bool; + + type SomeType; + + fn a(); + fn b(); + fn c(); +} + +#[derive(std::clone::Clone, Default)] +struct ZisShouldBeBeforeZeMainFn; + +fn main() { + // test code goes here +} + +#[cfg(test)] +mod test { + const B: i8 = 1; + + const A: i8 = 0; +} diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_good_var_1.rs b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good_var_1.rs new file mode 100644 index 00000000000..0fccbd4790b --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_good_var_1.rs @@ -0,0 +1,174 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: var_1 +//@[var_1] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/var_1 + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +/// This module gets linted before clippy gives up. +mod i_am_just_right { + const AFTER: i8 = 0; + + const BEFORE: i8 = 0; +} + +/// Since the upper module passes linting, the lint now recurses into this module. +mod this_is_in_the_wrong_position { + const A: i8 = 1; + const C: i8 = 0; +} + +// Use statements should not be linted internally - this is normally auto-sorted using rustfmt. +use std::rc::{Rc, Weak}; +use std::sync::{Arc, Barrier, RwLock}; + +const SNAKE_CASE: &str = "zzzzzzzz"; + +const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + +const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + +trait BasicEmptyTrait {} + +trait CloneSelf { + fn clone_self(&self) -> Self; +} + +enum EnumOrdered { + A, + B, + C, +} + +enum EnumUnordered { + A, + B, + C, +} + +#[allow(clippy::arbitrary_source_item_ordering)] +enum EnumUnorderedAllowed { + A, + B, + C, +} + +struct StructOrdered { + a: bool, + b: bool, + c: bool, +} + +impl BasicEmptyTrait for StructOrdered {} + +impl CloneSelf for StructOrdered { + fn clone_self(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl Default for StructOrdered { + fn default() -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl std::clone::Clone for StructOrdered { + fn clone(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +#[derive(Clone, Default)] +struct StructUnordered { + a: bool, + b: bool, + c: bool, + d: bool, +} + +impl TraitUnordered for StructUnordered { + fn a() {} + fn b() {} + fn c() {} + + type SomeType = (); + + const A: bool = false; + const B: bool = false; + const C: bool = false; +} + +impl TraitUnorderedItemKinds for StructUnordered { + fn a() {} + + type SomeType = (); + + const A: bool = false; +} + +struct StructUnorderedGeneric { + _1: std::marker::PhantomData, + a: bool, + b: bool, + c: bool, + d: bool, +} + +trait TraitOrdered { + fn a(); + fn b(); + fn c(); + + type SomeType; + + const A: bool; + const B: bool; + const C: bool; +} + +trait TraitUnordered { + fn a(); + fn b(); + fn c(); + + type SomeType; + + const A: bool; + const B: bool; + const C: bool; +} + +trait TraitUnorderedItemKinds { + fn a(); + + type SomeType; + + const A: bool; +} + +#[derive(std::clone::Clone, Default)] +struct ZisShouldBeBeforeZeMainFn; + +fn main() { + // test code goes here +} + +#[cfg(test)] +mod test { + const B: i8 = 1; + + const A: i8 = 0; +} diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr new file mode 100644 index 00000000000..062bf25ea62 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr @@ -0,0 +1,226 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:21:14 + | +LL | use std::rc::Weak; + | ^^^^ + | +note: should be placed before `SNAKE_CASE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:19:7 + | +LL | const SNAKE_CASE: &str = "zzzzzzzz"; + | ^^^^^^^^^^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:64:1 + | +LL | / impl CloneSelf for StructOrdered { +LL | | fn clone_self(&self) -> Self { +LL | | Self { +LL | | a: true, +... | +LL | | } +LL | | } + | |_^ + | +note: should be placed before the following item + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:54:1 + | +LL | / impl Default for StructOrdered { +LL | | fn default() -> Self { +LL | | Self { +LL | | a: true, +... | +LL | | } +LL | | } + | |_^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7 + | +LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `TraitUnorderedItemKinds` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:7 + | +LL | trait TraitUnorderedItemKinds { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:151:1 + | +LL | impl BasicEmptyTrait for StructOrdered {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before the following item + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:138:1 + | +LL | / impl TraitUnordered for StructUnordered { +LL | | const A: bool = false; +LL | | const C: bool = false; +LL | | const B: bool = false; +... | +LL | | fn b() {} +LL | | } + | |_^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:170:5 + | +LL | mod this_is_in_the_wrong_position { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `main` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:165:4 + | +LL | fn main() { + | ^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:178:7 + | +LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `ZisShouldBeBeforeZeMainFn` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:176:8 + | +LL | struct ZisShouldBeBeforeZeMainFn; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:12:11 + | +LL | const AFTER: i8 = 0; + | ^^^^^ + | +note: should be placed before `BEFORE` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:10:11 + | +LL | const BEFORE: i8 = 0; + | ^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:38:5 + | +LL | B, + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:37:5 + | +LL | C, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:88:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:87:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5 + | +LL | b: bool, + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5 + | +LL | c: bool, + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:115:11 + | +LL | const B: bool; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:114:11 + | +LL | const C: bool; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:8 + | +LL | fn b(); + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:8 + | +LL | fn c(); + | ^ + +error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:127:5 + | +LL | const A: bool; + | ^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:141:11 + | +LL | const B: bool = false; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:140:11 + | +LL | const C: bool = false; + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:147:8 + | +LL | fn b() {} + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:146:8 + | +LL | fn c() {} + | ^ + +error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:156:5 + | +LL | const A: bool = false; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:5 + | +LL | type SomeType = (); + | ^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:172:11 + | +LL | const A: i8 = 1; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11 + | +LL | const C: i8 = 0; + | ^ + +error: aborting due to 17 previous errors + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs new file mode 100644 index 00000000000..2765bf935b7 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs @@ -0,0 +1,185 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: default +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +/// This module gets linted before clippy gives up. +mod i_am_just_right { + const BEFORE: i8 = 0; + + const AFTER: i8 = 0; +} + +// Use statements should not be linted internally - this is normally auto-sorted using rustfmt. +use std::rc::Rc; +use std::sync::{Arc, Barrier, RwLock}; + +const SNAKE_CASE: &str = "zzzzzzzz"; + +use std::rc::Weak; + +trait BasicEmptyTrait {} + +trait CloneSelf { + fn clone_self(&self) -> Self; +} + +enum EnumOrdered { + A, + B, + C, +} + +enum EnumUnordered { + A, + C, + B, +} + +#[allow(clippy::arbitrary_source_item_ordering)] +enum EnumUnorderedAllowed { + A, + C, + B, +} + +struct StructOrdered { + a: bool, + b: bool, + c: bool, +} + +impl Default for StructOrdered { + fn default() -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl CloneSelf for StructOrdered { + fn clone_self(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl std::clone::Clone for StructOrdered { + fn clone(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +#[derive(Default, Clone)] +struct StructUnordered { + a: bool, + c: bool, + b: bool, + d: bool, +} + +struct StructUnorderedGeneric { + _1: std::marker::PhantomData, + a: bool, + c: bool, + b: bool, + d: bool, +} + +trait TraitOrdered { + const A: bool; + const B: bool; + const C: bool; + + type SomeType; + + fn a(); + fn b(); + fn c(); +} + +trait TraitUnordered { + const A: bool; + const C: bool; + const B: bool; + + type SomeType; + + fn a(); + fn c(); + fn b(); +} + +trait TraitUnorderedItemKinds { + type SomeType; + + const A: bool; + const B: bool; + const C: bool; + + fn a(); + fn b(); + fn c(); +} + +const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + +impl TraitUnordered for StructUnordered { + const A: bool = false; + const C: bool = false; + const B: bool = false; + + type SomeType = (); + + fn a() {} + fn c() {} + fn b() {} +} + +// Trait impls should be located just after the type they implement it for. +impl BasicEmptyTrait for StructOrdered {} + +impl TraitUnorderedItemKinds for StructUnordered { + type SomeType = (); + + const A: bool = false; + const B: bool = false; + const C: bool = false; + + fn a() {} + fn b() {} + fn c() {} +} + +fn main() { + // test code goes here +} + +/// Note that the linting pass is stopped before recursing into this module. +mod this_is_in_the_wrong_position { + const C: i8 = 0; + const A: i8 = 1; +} + +#[derive(Default, std::clone::Clone)] +struct ZisShouldBeBeforeZeMainFn; + +const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + +#[cfg(test)] +mod test { + const B: i8 = 1; + + const A: i8 = 0; +} diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs new file mode 100644 index 00000000000..44902336573 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs @@ -0,0 +1,174 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: var_1 +//@[var_1] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/var_1 + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +/// This module gets linted before clippy gives up. +mod i_am_just_right { + const AFTER: i8 = 0; + + const BEFORE: i8 = 0; +} + +/// Since the upper module passes linting, the lint now recurses into this module. +mod this_is_in_the_wrong_position { + const A: i8 = 1; + const C: i8 = 0; +} + +// Use statements should not be linted internally - this is normally auto-sorted using rustfmt. +use std::rc::{Rc, Weak}; +use std::sync::{Arc, Barrier, RwLock}; + +const SNAKE_CASE: &str = "zzzzzzzz"; + +const ZIS_SHOULD_BE_EVEN_EARLIER: () = (); + +const ZIS_SHOULD_BE_REALLY_EARLY: () = (); + +trait BasicEmptyTrait {} + +trait CloneSelf { + fn clone_self(&self) -> Self; +} + +enum EnumOrdered { + A, + B, + C, +} + +enum EnumUnordered { + A, + B, + C, +} + +#[allow(clippy::arbitrary_source_item_ordering)] +enum EnumUnorderedAllowed { + A, + B, + C, +} + +struct StructOrdered { + a: bool, + b: bool, + c: bool, +} + +impl BasicEmptyTrait for StructOrdered {} + +impl CloneSelf for StructOrdered { + fn clone_self(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl Default for StructOrdered { + fn default() -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +impl std::clone::Clone for StructOrdered { + fn clone(&self) -> Self { + Self { + a: true, + b: true, + c: true, + } + } +} + +#[derive(Clone, Default)] +struct StructUnordered { + a: bool, + b: bool, + c: bool, + d: bool, +} + +impl TraitUnordered for StructUnordered { + fn a() {} + fn c() {} + fn b() {} + + type SomeType = (); + + const A: bool = false; + const C: bool = false; + const B: bool = false; +} + +impl TraitUnorderedItemKinds for StructUnordered { + const A: bool = false; + + type SomeType = (); + + fn a() {} +} + +struct StructUnorderedGeneric { + _1: std::marker::PhantomData, + a: bool, + b: bool, + c: bool, + d: bool, +} + +trait TraitOrdered { + fn a(); + fn b(); + fn c(); + + type SomeType; + + const A: bool; + const B: bool; + const C: bool; +} + +trait TraitUnordered { + fn a(); + fn c(); + fn b(); + + type SomeType; + + const A: bool; + const C: bool; + const B: bool; +} + +trait TraitUnorderedItemKinds { + const A: bool; + + type SomeType; + + fn a(); +} + +#[derive(std::clone::Clone, Default)] +struct ZisShouldBeBeforeZeMainFn; + +fn main() { + // test code goes here +} + +#[cfg(test)] +mod test { + const B: i8 = 1; + + const A: i8 = 0; +} diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr new file mode 100644 index 00000000000..f31f7f68c17 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr @@ -0,0 +1,100 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:105:8 + | +LL | fn b() {} + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:104:8 + | +LL | fn c() {} + | ^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:111:11 + | +LL | const B: bool = false; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:110:11 + | +LL | const C: bool = false; + | ^ + +error: incorrect ordering of impl items (defined order: [Fn, Type, Const]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:117:5 + | +LL | type SomeType = (); + | ^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `A` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:115:5 + | +LL | const A: bool = false; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of impl items (defined order: [Fn, Type, Const]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:119:5 + | +LL | fn a() {} + | ^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:117:5 + | +LL | type SomeType = (); + | ^^^^^^^^^^^^^^^^^^^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:145:8 + | +LL | fn b(); + | ^ + | +note: should be placed before `c` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:144:8 + | +LL | fn c(); + | ^ + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:151:11 + | +LL | const B: bool; + | ^ + | +note: should be placed before `C` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:150:11 + | +LL | const C: bool; + | ^ + +error: incorrect ordering of trait items (defined order: [Fn, Type, Const]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:157:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + | +note: should be placed before `A` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:155:5 + | +LL | const A: bool; + | ^^^^^^^^^^^^^^ + +error: incorrect ordering of trait items (defined order: [Fn, Type, Const]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:159:5 + | +LL | fn a(); + | ^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:157:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.only_enum.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.only_enum.stderr new file mode 100644 index 00000000000..57069fd8fee --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.only_enum.stderr @@ -0,0 +1,16 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs:22:5 + | +LL | A, + | ^ + | +note: should be placed before `B` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs:21:5 + | +LL | B, + | ^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs b/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs new file mode 100644 index 00000000000..e8002c4a163 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs @@ -0,0 +1,43 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: only_enum +//@[only_enum] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/only_enum + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +fn main() {} + +struct StructUnordered { + b: bool, + a: bool, +} + +enum EnumOrdered { + A, + B, +} + +enum EnumUnordered { + B, + A, +} + +trait TraitUnordered { + const B: bool; + const A: bool; + + type SomeType; + + fn b(); + fn a(); +} + +trait TraitUnorderedItemKinds { + type SomeType; + + const A: bool; + + fn a(); +} + +const ZIS_SHOULD_BE_AT_THE_TOP: () = (); diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr new file mode 100644 index 00000000000..40348ecbdae --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr @@ -0,0 +1,40 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:43:8 + | +LL | fn a() {} + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:42:8 + | +LL | fn b() {} + | ^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:45:5 + | +LL | type SomeType = i8; + | ^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `a` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:43:5 + | +LL | fn a() {} + | ^^^^^^^^^ + +error: incorrect ordering of impl items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:47:5 + | +LL | const A: bool = true; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:45:5 + | +LL | type SomeType = i8; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs b/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs new file mode 100644 index 00000000000..bd969c865b5 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs @@ -0,0 +1,67 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: only_impl +//@[only_impl] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/only_impl + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +fn main() {} + +struct StructUnordered { + b: bool, + a: bool, +} + +struct BasicStruct {} + +trait BasicTrait { + const A: bool; + + type SomeType; + + fn b(); + fn a(); +} + +enum EnumUnordered { + B, + A, +} + +trait TraitUnordered { + const B: bool; + const A: bool; + + type SomeType; + + fn b(); + fn a(); +} + +impl BasicTrait for StructUnordered { + fn b() {} + fn a() {} + + type SomeType = i8; + + const A: bool = true; +} + +trait TraitUnorderedItemKinds { + type SomeType; + + const A: bool; + + fn a(); +} + +const ZIS_SHOULD_BE_AT_THE_TOP: () = (); + +impl BasicTrait for BasicStruct { + const A: bool = true; + + type SomeType = i8; + + fn a() {} + fn b() {} +} diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr b/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr new file mode 100644 index 00000000000..9b86ebd48e3 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr @@ -0,0 +1,40 @@ +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:32:11 + | +LL | const A: bool; + | ^ + | +note: should be placed before `B` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:31:11 + | +LL | const B: bool; + | ^ + = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]` + +error: incorrect ordering of items (must be alphabetically ordered) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:37:8 + | +LL | fn a(); + | ^ + | +note: should be placed before `b` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:36:8 + | +LL | fn b(); + | ^ + +error: incorrect ordering of trait items (defined order: [Const, Type, Fn]) + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:43:5 + | +LL | const A: bool; + | ^^^^^^^^^^^^^^ + | +note: should be placed before `SomeType` + --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:41:5 + | +LL | type SomeType; + | ^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs b/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs new file mode 100644 index 00000000000..979a52ecb10 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs @@ -0,0 +1,48 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs +//@revisions: only_trait +//@[only_trait] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/only_trait + +#![allow(dead_code)] +#![warn(clippy::arbitrary_source_item_ordering)] + +fn main() {} + +struct StructUnordered { + b: bool, + a: bool, +} + +trait TraitOrdered { + const A: bool; + const B: bool; + + type SomeType; + + fn a(); + fn b(); +} + +enum EnumUnordered { + B, + A, +} + +trait TraitUnordered { + const B: bool; + const A: bool; + + type SomeType; + + fn b(); + fn a(); +} + +trait TraitUnorderedItemKinds { + type SomeType; + + const A: bool; + + fn a(); +} + +const ZIS_SHOULD_BE_AT_THE_TOP: () = (); diff --git a/tests/ui-toml/arbitrary_source_item_ordering/var_1/clippy.toml b/tests/ui-toml/arbitrary_source_item_ordering/var_1/clippy.toml new file mode 100644 index 00000000000..bede035e388 --- /dev/null +++ b/tests/ui-toml/arbitrary_source_item_ordering/var_1/clippy.toml @@ -0,0 +1 @@ +trait-assoc-item-kinds-order = ["fn", "type", "const"] diff --git a/tests/ui-toml/large_include_file/large_include_file.rs b/tests/ui-toml/large_include_file/large_include_file.rs index f3dbb6ad1cf..dc9349f75a0 100644 --- a/tests/ui-toml/large_include_file/large_include_file.rs +++ b/tests/ui-toml/large_include_file/large_include_file.rs @@ -1,8 +1,8 @@ #![warn(clippy::large_include_file)] // Good -const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs"); -const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs"); +const GOOD_INCLUDE_BYTES: &[u8; 68] = include_bytes!("../../ui/author.rs"); +const GOOD_INCLUDE_STR: &str = include_str!("../../ui/author.rs"); #[allow(clippy::large_include_file)] const ALLOWED_TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); @@ -11,6 +11,9 @@ // Bad const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); +//~^ large_include_file const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); +//~^ large_include_file +#[doc = include_str!("too_big.txt")] //~ large_include_file fn main() {} diff --git a/tests/ui-toml/large_include_file/large_include_file.stderr b/tests/ui-toml/large_include_file/large_include_file.stderr index 34224065f07..9e1494a47bb 100644 --- a/tests/ui-toml/large_include_file/large_include_file.stderr +++ b/tests/ui-toml/large_include_file/large_include_file.stderr @@ -9,12 +9,20 @@ LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); = help: to override `-D warnings` add `#[allow(clippy::large_include_file)]` error: attempted to include a large file - --> tests/ui-toml/large_include_file/large_include_file.rs:14:35 + --> tests/ui-toml/large_include_file/large_include_file.rs:15:35 | LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the configuration allows a maximum size of 600 bytes -error: aborting due to 2 previous errors +error: attempted to include a large file + --> tests/ui-toml/large_include_file/large_include_file.rs:18:1 + | +LL | #[doc = include_str!("too_big.txt")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the configuration allows a maximum size of 600 bytes + +error: aborting due to 3 previous errors diff --git a/tests/ui-toml/private-doc-errors/doc_lints.rs b/tests/ui-toml/private-doc-errors/doc_lints.rs index 79c8751468d..c5d00c91b05 100644 --- a/tests/ui-toml/private-doc-errors/doc_lints.rs +++ b/tests/ui-toml/private-doc-errors/doc_lints.rs @@ -51,4 +51,10 @@ pub unsafe fn f() {} } } +#[warn(clippy::missing_errors_doc)] +#[test] +fn test() -> Result<(), ()> { + Ok(()) +} + fn main() {} diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 5cf9c0fb271..6fa583fc041 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -54,12 +54,14 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect max-trait-bounds min-ident-chars-threshold missing-docs-in-crate-items + module-item-order-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold + source-item-ordering stack-size-threshold standard-macro-braces struct-field-name-threshold @@ -68,6 +70,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect too-large-for-stack too-many-arguments-threshold too-many-lines-threshold + trait-assoc-item-kinds-order trivial-copy-size-limit type-complexity-threshold unnecessary-box-size @@ -138,12 +141,14 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect max-trait-bounds min-ident-chars-threshold missing-docs-in-crate-items + module-item-order-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold + source-item-ordering stack-size-threshold standard-macro-braces struct-field-name-threshold @@ -152,6 +157,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect too-large-for-stack too-many-arguments-threshold too-many-lines-threshold + trait-assoc-item-kinds-order trivial-copy-size-limit type-complexity-threshold unnecessary-box-size @@ -222,12 +228,14 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni max-trait-bounds min-ident-chars-threshold missing-docs-in-crate-items + module-item-order-groupings msrv pass-by-value-size-limit pub-underscore-fields-behavior semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold + source-item-ordering stack-size-threshold standard-macro-braces struct-field-name-threshold @@ -236,6 +244,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni too-large-for-stack too-many-arguments-threshold too-many-lines-threshold + trait-assoc-item-kinds-order trivial-copy-size-limit type-complexity-threshold unnecessary-box-size diff --git a/tests/ui/allow_attributes.fixed b/tests/ui/allow_attributes.fixed index 058dbb77a32..8f6c962e2f5 100644 --- a/tests/ui/allow_attributes.fixed +++ b/tests/ui/allow_attributes.fixed @@ -1,4 +1,5 @@ //@aux-build:proc_macros.rs +//@aux-build:proc_macro_derive.rs #![allow(unused)] #![warn(clippy::allow_attributes)] #![no_main] @@ -65,3 +66,9 @@ fn deny_allow_attributes() -> Option { allow?; Some(42) } + +// Edge case where the generated tokens spans match on #[repr(transparent)] which tricks the proc +// macro check +#[repr(transparent)] +#[derive(proc_macro_derive::AllowLintSameSpan)] // This macro generates tokens with the same span as the whole struct and repr +struct IgnoreDerived; diff --git a/tests/ui/allow_attributes.rs b/tests/ui/allow_attributes.rs index 6d94ce50e4c..cb6c4dcf715 100644 --- a/tests/ui/allow_attributes.rs +++ b/tests/ui/allow_attributes.rs @@ -1,4 +1,5 @@ //@aux-build:proc_macros.rs +//@aux-build:proc_macro_derive.rs #![allow(unused)] #![warn(clippy::allow_attributes)] #![no_main] @@ -65,3 +66,9 @@ fn deny_allow_attributes() -> Option { allow?; Some(42) } + +// Edge case where the generated tokens spans match on #[repr(transparent)] which tricks the proc +// macro check +#[repr(transparent)] +#[derive(proc_macro_derive::AllowLintSameSpan)] // This macro generates tokens with the same span as the whole struct and repr +struct IgnoreDerived; diff --git a/tests/ui/allow_attributes.stderr b/tests/ui/allow_attributes.stderr index 023b4d7e404..5a4ff287acd 100644 --- a/tests/ui/allow_attributes.stderr +++ b/tests/ui/allow_attributes.stderr @@ -1,5 +1,5 @@ error: #[allow] attribute found - --> tests/ui/allow_attributes.rs:12:3 + --> tests/ui/allow_attributes.rs:13:3 | LL | #[allow(dead_code)] | ^^^^^ help: replace it with: `expect` @@ -8,13 +8,13 @@ LL | #[allow(dead_code)] = help: to override `-D warnings` add `#[allow(clippy::allow_attributes)]` error: #[allow] attribute found - --> tests/ui/allow_attributes.rs:21:30 + --> tests/ui/allow_attributes.rs:22:30 | LL | #[cfg_attr(panic = "unwind", allow(dead_code))] | ^^^^^ help: replace it with: `expect` error: #[allow] attribute found - --> tests/ui/allow_attributes.rs:52:7 + --> tests/ui/allow_attributes.rs:53:7 | LL | #[allow(unused)] | ^^^^^ help: replace it with: `expect` diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index bd90042c1da..fbf84337382 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -1,4 +1,4 @@ -#![feature(repr128, proc_macro_quote)] +#![feature(repr128, proc_macro_quote, proc_macro_span)] #![allow(incomplete_features)] #![allow(clippy::field_reassign_with_default)] #![allow(clippy::eq_op)] @@ -182,3 +182,51 @@ fn clone(&self) -> Self { impl Copy for NonCanonicalClone {} } } + +// Derive macro that generates the following but where all generated spans are set to the entire +// input span. +// +// ``` +// #[allow(clippy::missing_const_for_fn)] +// fn check() {} +// ``` +#[proc_macro_derive(AllowLintSameSpan)] +pub fn allow_lint_same_span_derive(input: TokenStream) -> TokenStream { + let mut iter = input.into_iter(); + let first = iter.next().unwrap(); + let last = iter.last().unwrap(); + let span = first.span().join(last.span()).unwrap(); + let span_help = |mut t: TokenTree| -> TokenTree { + t.set_span(span); + t + }; + // Generate the TokenStream but setting all the spans to the entire input span + >::from_iter([ + span_help(Punct::new('#', Spacing::Alone).into()), + span_help( + Group::new( + Delimiter::Bracket, + >::from_iter([ + Ident::new("allow", span).into(), + span_help( + Group::new( + Delimiter::Parenthesis, + >::from_iter([ + Ident::new("clippy", span).into(), + span_help(Punct::new(':', Spacing::Joint).into()), + span_help(Punct::new(':', Spacing::Alone).into()), + Ident::new("missing_const_for_fn", span).into(), + ]), + ) + .into(), + ), + ]), + ) + .into(), + ), + Ident::new("fn", span).into(), + Ident::new("check", span).into(), + span_help(Group::new(Delimiter::Parenthesis, TokenStream::new()).into()), + span_help(Group::new(Delimiter::Brace, TokenStream::new()).into()), + ]) +} diff --git a/tests/ui/borrow_deref_ref.fixed b/tests/ui/borrow_deref_ref.fixed index ea5e983de3b..22e984c46d2 100644 --- a/tests/ui/borrow_deref_ref.fixed +++ b/tests/ui/borrow_deref_ref.fixed @@ -71,3 +71,9 @@ mod false_negative { assert_ne!(addr_x, addr_y); } } + +fn issue_13584() { + let s = "Hello, world!\n"; + let p = &raw const *s; + let _ = p as *const i8; +} diff --git a/tests/ui/borrow_deref_ref.rs b/tests/ui/borrow_deref_ref.rs index 8c8905b150e..61d89193f42 100644 --- a/tests/ui/borrow_deref_ref.rs +++ b/tests/ui/borrow_deref_ref.rs @@ -71,3 +71,9 @@ fn foo() { assert_ne!(addr_x, addr_y); } } + +fn issue_13584() { + let s = "Hello, world!\n"; + let p = &raw const *s; + let _ = p as *const i8; +} diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index 04e0de91ecf..b5e27b61548 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -171,3 +171,17 @@ fn constant_from_external_crate() { let _ = std::env::consts::EXE_EXTENSION.is_empty(); // Do not lint, `exe_ext` comes from the `std` crate } + +fn issue_13106() { + const { + assert!(!NON_EMPTY_STR.is_empty()); + } + + const { + assert!(EMPTY_STR.is_empty()); + } + + const { + EMPTY_STR.is_empty(); + } +} diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr index 7f80b520b1a..0afba940d8b 100644 --- a/tests/ui/const_is_empty.stderr +++ b/tests/ui/const_is_empty.stderr @@ -157,5 +157,11 @@ error: this expression always evaluates to true LL | let _ = val.is_empty(); | ^^^^^^^^^^^^^^ -error: aborting due to 26 previous errors +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:185:9 + | +LL | EMPTY_STR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 27 previous errors diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index b06dd78608f..3647b242505 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -6,7 +6,6 @@ )] #![warn(clippy::expl_impl_clone_on_copy)] - #[derive(Copy)] struct Qux; diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index 0eb4b3c1ada..c072a9a6277 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -1,5 +1,5 @@ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:13:1 + --> tests/ui/derive.rs:12:1 | LL | / impl Clone for Qux { LL | | @@ -10,7 +10,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:13:1 + --> tests/ui/derive.rs:12:1 | LL | / impl Clone for Qux { LL | | @@ -23,7 +23,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:38:1 + --> tests/ui/derive.rs:37:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -34,7 +34,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:38:1 + --> tests/ui/derive.rs:37:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -45,7 +45,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:50:1 + --> tests/ui/derive.rs:49:1 | LL | / impl Clone for BigArray { LL | | @@ -56,7 +56,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:50:1 + --> tests/ui/derive.rs:49:1 | LL | / impl Clone for BigArray { LL | | @@ -67,7 +67,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:62:1 + --> tests/ui/derive.rs:61:1 | LL | / impl Clone for FnPtr { LL | | @@ -78,7 +78,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:62:1 + --> tests/ui/derive.rs:61:1 | LL | / impl Clone for FnPtr { LL | | @@ -89,7 +89,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:83:1 + --> tests/ui/derive.rs:82:1 | LL | / impl Clone for Generic2 { LL | | @@ -100,7 +100,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:83:1 + --> tests/ui/derive.rs:82:1 | LL | / impl Clone for Generic2 { LL | | diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index 355f2bc7736..5cf5c608a85 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -54,8 +54,9 @@ fn test_units() { /// This tests allowed identifiers. /// KiB MiB GiB TiB PiB EiB +/// MHz GHz THz /// AccessKit -/// CoreFoundation CoreGraphics CoreText +/// CoAP CoreFoundation CoreGraphics CoreText /// Direct2D Direct3D DirectWrite DirectX /// ECMAScript /// GPLv2 GPLv3 diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 9ced2677622..420211c6539 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -54,8 +54,9 @@ fn test_units() { /// This tests allowed identifiers. /// KiB MiB GiB TiB PiB EiB +/// MHz GHz THz /// AccessKit -/// CoreFoundation CoreGraphics CoreText +/// CoAP CoreFoundation CoreGraphics CoreText /// Direct2D Direct3D DirectWrite DirectX /// ECMAScript /// GPLv2 GPLv3 diff --git a/tests/ui/doc/doc-fixable.stderr b/tests/ui/doc/doc-fixable.stderr index 67c0464149c..27a04e4b558 100644 --- a/tests/ui/doc/doc-fixable.stderr +++ b/tests/ui/doc/doc-fixable.stderr @@ -133,7 +133,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:78:5 + --> tests/ui/doc/doc-fixable.rs:79:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:95:5 + --> tests/ui/doc/doc-fixable.rs:96:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -155,7 +155,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:103:8 + --> tests/ui/doc/doc-fixable.rs:104:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | /// ## `CamelCaseThing` | ~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:106:7 + --> tests/ui/doc/doc-fixable.rs:107:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | /// # `CamelCaseThing` | ~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:108:22 + --> tests/ui/doc/doc-fixable.rs:109:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ @@ -188,7 +188,7 @@ LL | /// Not a title #897 `CamelCaseThing` | ~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:109:5 + --> tests/ui/doc/doc-fixable.rs:110:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -199,7 +199,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:116:5 + --> tests/ui/doc/doc-fixable.rs:117:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -210,7 +210,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:129:5 + --> tests/ui/doc/doc-fixable.rs:130:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -221,7 +221,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:140:43 + --> tests/ui/doc/doc-fixable.rs:141:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -232,7 +232,7 @@ LL | /** E.g., serialization of an empty list: `FooBar` | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:145:5 + --> tests/ui/doc/doc-fixable.rs:146:5 | LL | And BarQuz too. | ^^^^^^ @@ -243,7 +243,7 @@ LL | And `BarQuz` too. | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:146:1 + --> tests/ui/doc/doc-fixable.rs:147:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -254,7 +254,7 @@ LL | `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:151:43 + --> tests/ui/doc/doc-fixable.rs:152:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -265,7 +265,7 @@ LL | /** E.g., serialization of an empty list: `FooBar` | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:156:5 + --> tests/ui/doc/doc-fixable.rs:157:5 | LL | And BarQuz too. | ^^^^^^ @@ -276,7 +276,7 @@ LL | And `BarQuz` too. | ~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:157:1 + --> tests/ui/doc/doc-fixable.rs:158:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -287,7 +287,7 @@ LL | `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:168:5 + --> tests/ui/doc/doc-fixable.rs:169:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -298,7 +298,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:187:22 + --> tests/ui/doc/doc-fixable.rs:188:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ @@ -309,7 +309,7 @@ LL | /// An iterator over `mycrate::Collection`'s values. | ~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:211:34 + --> tests/ui/doc/doc-fixable.rs:212:34 | LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint | ^^^^^^^^^^^^^^^ @@ -320,7 +320,7 @@ LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` | ~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:234:22 + --> tests/ui/doc/doc-fixable.rs:235:22 | LL | /// There is no try (do() or do_not()). | ^^^^ @@ -331,7 +331,7 @@ LL | /// There is no try (`do()` or do_not()). | ~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:234:30 + --> tests/ui/doc/doc-fixable.rs:235:30 | LL | /// There is no try (do() or do_not()). | ^^^^^^^^ @@ -342,7 +342,7 @@ LL | /// There is no try (do() or `do_not()`). | ~~~~~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:237:5 + --> tests/ui/doc/doc-fixable.rs:238:5 | LL | /// ABes | ^^^^ @@ -353,7 +353,7 @@ LL | /// `ABes` | ~~~~~~ error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:243:9 + --> tests/ui/doc/doc-fixable.rs:244:9 | LL | /// foo() | ^^^^^ @@ -364,7 +364,7 @@ LL | /// `foo()` | ~~~~~~~ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> tests/ui/doc/doc-fixable.rs:247:5 + --> tests/ui/doc/doc-fixable.rs:248:5 | LL | /// https://github.com/rust-lang/rust-clippy/pull/12836 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `` diff --git a/tests/ui/infinite_loops.rs b/tests/ui/infinite_loops.rs index b6cb7ff49b0..0613eddce26 100644 --- a/tests/ui/infinite_loops.rs +++ b/tests/ui/infinite_loops.rs @@ -3,6 +3,7 @@ #![allow(clippy::never_loop)] #![warn(clippy::infinite_loop)] +#![feature(async_closure)] extern crate proc_macros; use proc_macros::{external, with_span}; @@ -428,4 +429,23 @@ fn continue_outer() { } } +// don't suggest adding `-> !` to async fn/closure that already returning `-> !` +mod issue_12338 { + use super::do_something; + + async fn foo() -> ! { + loop { + do_something(); + } + } + + fn bar() { + let _ = async || -> ! { + loop { + do_something(); + } + }; + } +} + fn main() {} diff --git a/tests/ui/infinite_loops.stderr b/tests/ui/infinite_loops.stderr index 7635a7442f4..3a3ed7d0fe8 100644 --- a/tests/ui/infinite_loops.stderr +++ b/tests/ui/infinite_loops.stderr @@ -1,5 +1,5 @@ error: infinite loop detected - --> tests/ui/infinite_loops.rs:13:5 + --> tests/ui/infinite_loops.rs:14:5 | LL | / loop { LL | | @@ -15,7 +15,7 @@ LL | fn no_break() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:20:5 + --> tests/ui/infinite_loops.rs:21:5 | LL | / loop { LL | | @@ -32,7 +32,7 @@ LL | fn all_inf() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:22:9 + --> tests/ui/infinite_loops.rs:23:9 | LL | / loop { LL | | @@ -49,7 +49,7 @@ LL | fn all_inf() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:24:13 + --> tests/ui/infinite_loops.rs:25:13 | LL | / loop { LL | | @@ -63,7 +63,7 @@ LL | fn all_inf() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:38:5 + --> tests/ui/infinite_loops.rs:39:5 | LL | / loop { LL | | @@ -74,7 +74,7 @@ LL | | } = help: if this is not intended, try adding a `break` or `return` condition in the loop error: infinite loop detected - --> tests/ui/infinite_loops.rs:51:5 + --> tests/ui/infinite_loops.rs:52:5 | LL | / loop { LL | | fn inner_fn() -> ! { @@ -90,7 +90,7 @@ LL | fn no_break_never_ret_noise() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:94:5 + --> tests/ui/infinite_loops.rs:95:5 | LL | / loop { LL | | @@ -107,7 +107,7 @@ LL | fn break_inner_but_not_outer_1(cond: bool) -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:105:5 + --> tests/ui/infinite_loops.rs:106:5 | LL | / loop { LL | | @@ -124,7 +124,7 @@ LL | fn break_inner_but_not_outer_2(cond: bool) -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:119:9 + --> tests/ui/infinite_loops.rs:120:9 | LL | / loop { LL | | @@ -138,7 +138,7 @@ LL | fn break_outer_but_not_inner() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:142:9 + --> tests/ui/infinite_loops.rs:143:9 | LL | / loop { LL | | @@ -155,7 +155,7 @@ LL | fn break_wrong_loop(cond: bool) -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:182:5 + --> tests/ui/infinite_loops.rs:183:5 | LL | / loop { LL | | @@ -172,7 +172,7 @@ LL | fn match_like() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:223:5 + --> tests/ui/infinite_loops.rs:224:5 | LL | / loop { LL | | @@ -186,7 +186,7 @@ LL | fn match_like() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:228:5 + --> tests/ui/infinite_loops.rs:229:5 | LL | / loop { LL | | @@ -203,7 +203,7 @@ LL | fn match_like() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:333:9 + --> tests/ui/infinite_loops.rs:334:9 | LL | / loop { LL | | @@ -217,7 +217,7 @@ LL | fn problematic_trait_method() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:343:9 + --> tests/ui/infinite_loops.rs:344:9 | LL | / loop { LL | | @@ -231,7 +231,7 @@ LL | fn could_be_problematic() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:352:9 + --> tests/ui/infinite_loops.rs:353:9 | LL | / loop { LL | | @@ -245,7 +245,7 @@ LL | let _loop_forever = || -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:366:8 + --> tests/ui/infinite_loops.rs:367:8 | LL | Ok(loop { | ________^ @@ -256,7 +256,7 @@ LL | | }) = help: if this is not intended, try adding a `break` or `return` condition in the loop error: infinite loop detected - --> tests/ui/infinite_loops.rs:408:5 + --> tests/ui/infinite_loops.rs:409:5 | LL | / 'infinite: loop { LL | | @@ -272,7 +272,7 @@ LL | fn continue_outer() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:415:5 + --> tests/ui/infinite_loops.rs:416:5 | LL | / loop { LL | | @@ -289,7 +289,7 @@ LL | fn continue_outer() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:417:9 + --> tests/ui/infinite_loops.rs:418:9 | LL | / 'inner: loop { LL | | loop { @@ -304,7 +304,7 @@ LL | fn continue_outer() -> ! { | ++++ error: infinite loop detected - --> tests/ui/infinite_loops.rs:425:5 + --> tests/ui/infinite_loops.rs:426:5 | LL | / loop { LL | | diff --git a/tests/ui/iter_without_into_iter.stderr b/tests/ui/iter_without_into_iter.stderr index 7c42fa1dd89..d748c85003b 100644 --- a/tests/ui/iter_without_into_iter.stderr +++ b/tests/ui/iter_without_into_iter.stderr @@ -13,8 +13,8 @@ help: consider implementing `IntoIterator` for `&S1` | LL + LL + impl IntoIterator for &S1 { -LL + type IntoIter = std::slice::Iter<'_, u8>; LL + type Item = &u8; +LL + type IntoIter = std::slice::Iter<'_, u8>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -34,8 +34,8 @@ help: consider implementing `IntoIterator` for `&mut S1` | LL + LL + impl IntoIterator for &mut S1 { -LL + type IntoIter = std::slice::IterMut<'_, u8>; LL + type Item = &mut u8; +LL + type IntoIter = std::slice::IterMut<'_, u8>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -55,8 +55,8 @@ help: consider implementing `IntoIterator` for `&S3<'a>` | LL + LL + impl IntoIterator for &S3<'a> { -LL + type IntoIter = std::slice::Iter<'_, u8>; LL + type Item = &u8; +LL + type IntoIter = std::slice::Iter<'_, u8>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -76,8 +76,8 @@ help: consider implementing `IntoIterator` for `&mut S3<'a>` | LL + LL + impl IntoIterator for &mut S3<'a> { -LL + type IntoIter = std::slice::IterMut<'_, u8>; LL + type Item = &mut u8; +LL + type IntoIter = std::slice::IterMut<'_, u8>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -96,8 +96,8 @@ help: consider implementing `IntoIterator` for `&S8` | LL + LL + impl IntoIterator for &S8 { -LL + type IntoIter = std::slice::Iter<'static, T>; LL + type Item = &T; +LL + type IntoIter = std::slice::Iter<'static, T>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -117,8 +117,8 @@ help: consider implementing `IntoIterator` for `&S9` | LL + LL + impl IntoIterator for &S9 { -LL + type IntoIter = std::slice::Iter<'_, T>; LL + type Item = &T; +LL + type IntoIter = std::slice::Iter<'_, T>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -138,8 +138,8 @@ help: consider implementing `IntoIterator` for `&mut S9` | LL + LL + impl IntoIterator for &mut S9 { -LL + type IntoIter = std::slice::IterMut<'_, T>; LL + type Item = &mut T; +LL + type IntoIter = std::slice::IterMut<'_, T>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } @@ -162,8 +162,8 @@ help: consider implementing `IntoIterator` for `&Issue12037` | LL ~ LL + impl IntoIterator for &Issue12037 { -LL + type IntoIter = std::slice::Iter<'_, u8>; LL + type Item = &u8; +LL + type IntoIter = std::slice::Iter<'_, u8>; LL + fn into_iter(self) -> Self::IntoIter { LL + self.iter() LL + } diff --git a/tests/ui/large_const_arrays.fixed b/tests/ui/large_const_arrays.fixed index 543ce460e7b..e5b28cb6a9d 100644 --- a/tests/ui/large_const_arrays.fixed +++ b/tests/ui/large_const_arrays.fixed @@ -9,11 +9,13 @@ pub struct S { // Should lint pub(crate) static FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000]; pub static FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; +static FOO_COMPUTED: [u32; 1_000 * 100] = [0u32; 1_000 * 100]; static FOO: [u32; 1_000_000] = [0u32; 1_000_000]; // Good pub(crate) const G_FOO_PUB_CRATE: [u32; 250] = [0u32; 250]; pub const G_FOO_PUB: [u32; 250] = [0u32; 250]; +const G_FOO_COMPUTED: [u32; 25 * 10] = [0u32; 25 * 10]; const G_FOO: [u32; 250] = [0u32; 250]; fn main() { diff --git a/tests/ui/large_const_arrays.rs b/tests/ui/large_const_arrays.rs index e23a8081171..b9593225da2 100644 --- a/tests/ui/large_const_arrays.rs +++ b/tests/ui/large_const_arrays.rs @@ -9,11 +9,13 @@ pub struct S { // Should lint pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000]; pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; +const FOO_COMPUTED: [u32; 1_000 * 100] = [0u32; 1_000 * 100]; const FOO: [u32; 1_000_000] = [0u32; 1_000_000]; // Good pub(crate) const G_FOO_PUB_CRATE: [u32; 250] = [0u32; 250]; pub const G_FOO_PUB: [u32; 250] = [0u32; 250]; +const G_FOO_COMPUTED: [u32; 25 * 10] = [0u32; 25 * 10]; const G_FOO: [u32; 250] = [0u32; 250]; fn main() { diff --git a/tests/ui/large_const_arrays.stderr b/tests/ui/large_const_arrays.stderr index 88f921b81f7..27112205390 100644 --- a/tests/ui/large_const_arrays.stderr +++ b/tests/ui/large_const_arrays.stderr @@ -20,13 +20,21 @@ LL | pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; error: large array defined as const --> tests/ui/large_const_arrays.rs:12:1 | +LL | const FOO_COMPUTED: [u32; 1_000 * 100] = [0u32; 1_000 * 100]; + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> tests/ui/large_const_arrays.rs:13:1 + | LL | const FOO: [u32; 1_000_000] = [0u32; 1_000_000]; | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | help: make this a static item: `static` error: large array defined as const - --> tests/ui/large_const_arrays.rs:21:5 + --> tests/ui/large_const_arrays.rs:23:5 | LL | pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,7 +42,7 @@ LL | pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; | help: make this a static item: `static` error: large array defined as const - --> tests/ui/large_const_arrays.rs:22:5 + --> tests/ui/large_const_arrays.rs:24:5 | LL | const BAR: [u32; 1_000_000] = [0u32; 1_000_000]; | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -42,7 +50,7 @@ LL | const BAR: [u32; 1_000_000] = [0u32; 1_000_000]; | help: make this a static item: `static` error: large array defined as const - --> tests/ui/large_const_arrays.rs:23:5 + --> tests/ui/large_const_arrays.rs:25:5 | LL | pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000]; | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -50,7 +58,7 @@ LL | pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000]; | help: make this a static item: `static` error: large array defined as const - --> tests/ui/large_const_arrays.rs:24:5 + --> tests/ui/large_const_arrays.rs:26:5 | LL | const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000]; | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,7 +66,7 @@ LL | const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000]; | help: make this a static item: `static` error: large array defined as const - --> tests/ui/large_const_arrays.rs:25:5 + --> tests/ui/large_const_arrays.rs:27:5 | LL | pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000]; | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -66,12 +74,12 @@ LL | pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000]; | help: make this a static item: `static` error: large array defined as const - --> tests/ui/large_const_arrays.rs:26:5 + --> tests/ui/large_const_arrays.rs:28:5 | LL | const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000]; | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | help: make this a static item: `static` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/manual_bits.fixed b/tests/ui/manual_bits.fixed index 4de01905e8a..8e5cb7d38b9 100644 --- a/tests/ui/manual_bits.fixed +++ b/tests/ui/manual_bits.fixed @@ -56,3 +56,14 @@ fn main() { let _ = (u128::BITS as usize).pow(5); let _ = &(u128::BITS as usize); } + +fn should_not_lint() { + macro_rules! bits_via_macro { + ($T: ty) => { + size_of::<$T>() * 8; + }; + } + + bits_via_macro!(u8); + bits_via_macro!(String); +} diff --git a/tests/ui/manual_bits.rs b/tests/ui/manual_bits.rs index d4f369fcf87..5e492ed15d2 100644 --- a/tests/ui/manual_bits.rs +++ b/tests/ui/manual_bits.rs @@ -56,3 +56,14 @@ fn main() { let _ = (size_of::() * 8).pow(5); let _ = &(size_of::() * 8); } + +fn should_not_lint() { + macro_rules! bits_via_macro { + ($T: ty) => { + size_of::<$T>() * 8; + }; + } + + bits_via_macro!(u8); + bits_via_macro!(String); +} diff --git a/tests/ui/map_all_any_identity.fixed b/tests/ui/map_all_any_identity.fixed new file mode 100644 index 00000000000..c92462ab9c2 --- /dev/null +++ b/tests/ui/map_all_any_identity.fixed @@ -0,0 +1,21 @@ +#![warn(clippy::map_all_any_identity)] + +fn main() { + let _ = ["foo"].into_iter().any(|s| s == "foo"); + //~^ map_all_any_identity + let _ = ["foo"].into_iter().all(|s| s == "foo"); + //~^ map_all_any_identity + + // + // Do not lint + // + // Not identity + let _ = ["foo"].into_iter().map(|s| s.len()).any(|n| n > 0); + // Macro + macro_rules! map { + ($x:expr) => { + $x.into_iter().map(|s| s == "foo") + }; + } + map!(["foo"]).any(|a| a); +} diff --git a/tests/ui/map_all_any_identity.rs b/tests/ui/map_all_any_identity.rs new file mode 100644 index 00000000000..0e4a564ca04 --- /dev/null +++ b/tests/ui/map_all_any_identity.rs @@ -0,0 +1,21 @@ +#![warn(clippy::map_all_any_identity)] + +fn main() { + let _ = ["foo"].into_iter().map(|s| s == "foo").any(|a| a); + //~^ map_all_any_identity + let _ = ["foo"].into_iter().map(|s| s == "foo").all(std::convert::identity); + //~^ map_all_any_identity + + // + // Do not lint + // + // Not identity + let _ = ["foo"].into_iter().map(|s| s.len()).any(|n| n > 0); + // Macro + macro_rules! map { + ($x:expr) => { + $x.into_iter().map(|s| s == "foo") + }; + } + map!(["foo"]).any(|a| a); +} diff --git a/tests/ui/map_all_any_identity.stderr b/tests/ui/map_all_any_identity.stderr new file mode 100644 index 00000000000..98fdcc2a939 --- /dev/null +++ b/tests/ui/map_all_any_identity.stderr @@ -0,0 +1,26 @@ +error: usage of `.map(...).any(identity)` + --> tests/ui/map_all_any_identity.rs:4:33 + | +LL | let _ = ["foo"].into_iter().map(|s| s == "foo").any(|a| a); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::map-all-any-identity` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::map_all_any_identity)]` +help: use `.any(...)` instead + | +LL | let _ = ["foo"].into_iter().any(|s| s == "foo"); + | ~~~~~~~~~~~~~~~~~~~ + +error: usage of `.map(...).all(identity)` + --> tests/ui/map_all_any_identity.rs:6:33 + | +LL | let _ = ["foo"].into_iter().map(|s| s == "foo").all(std::convert::identity); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `.all(...)` instead + | +LL | let _ = ["foo"].into_iter().all(|s| s == "foo"); + | ~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/map_with_unused_argument_over_ranges.fixed b/tests/ui/map_with_unused_argument_over_ranges.fixed new file mode 100644 index 00000000000..cf520e71a64 --- /dev/null +++ b/tests/ui/map_with_unused_argument_over_ranges.fixed @@ -0,0 +1,73 @@ +#![allow( + unused, + clippy::redundant_closure, + clippy::reversed_empty_ranges, + clippy::identity_op +)] +#![warn(clippy::map_with_unused_argument_over_ranges)] + +fn do_something() -> usize { + todo!() +} + +fn do_something_interesting(x: usize, y: usize) -> usize { + todo!() +} + +macro_rules! gen { + () => { + (0..10).map(|_| do_something()); + }; +} + +fn main() { + // These should be raised + std::iter::repeat_with(|| do_something()).take(10); + std::iter::repeat_with(|| do_something()).take(10); + std::iter::repeat_with(|| do_something()).take(11); + std::iter::repeat_with(|| do_something()).take(7); + std::iter::repeat_with(|| do_something()).take(8); + std::iter::repeat_n(3, 10); + std::iter::repeat_with(|| { + let x = 3; + x + 2 + }).take(10); + std::iter::repeat_with(|| do_something()).take(10).collect::>(); + let upper = 4; + std::iter::repeat_with(|| do_something()).take(upper); + let upper_fn = || 4; + std::iter::repeat_with(|| do_something()).take(upper_fn()); + std::iter::repeat_with(|| do_something()).take(upper_fn() + 1); + std::iter::repeat_with(|| do_something()).take(upper_fn() - 2); + std::iter::repeat_with(|| do_something()).take(upper_fn() - 1); + (-3..9).map(|_| do_something()); + std::iter::repeat_with(|| do_something()).take(0); + std::iter::repeat_with(|| do_something()).take(1); + std::iter::repeat_with(|| do_something()).take((1 << 4) - 0); + // These should not be raised + gen!(); + let lower = 2; + let lower_fn = || 2; + (lower..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled + (lower_fn()..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled + (lower_fn()..=upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled + (0..10).map(|x| do_something_interesting(x, 4)); // Actual map over range + "Foobar".chars().map(|_| do_something()); // Not a map over range + // i128::MAX == 340282366920938463463374607431768211455 + (0..=340282366920938463463374607431768211455).map(|_: u128| do_something()); // Can't be replaced due to overflow +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + (0..10).map(|_| do_something()); +} + +#[clippy::msrv = "1.28"] +fn msrv_1_28() { + std::iter::repeat_with(|| do_something()).take(10); +} + +#[clippy::msrv = "1.81"] +fn msrv_1_82() { + std::iter::repeat(3).take(10); +} diff --git a/tests/ui/map_with_unused_argument_over_ranges.rs b/tests/ui/map_with_unused_argument_over_ranges.rs new file mode 100644 index 00000000000..298eee9ca3f --- /dev/null +++ b/tests/ui/map_with_unused_argument_over_ranges.rs @@ -0,0 +1,73 @@ +#![allow( + unused, + clippy::redundant_closure, + clippy::reversed_empty_ranges, + clippy::identity_op +)] +#![warn(clippy::map_with_unused_argument_over_ranges)] + +fn do_something() -> usize { + todo!() +} + +fn do_something_interesting(x: usize, y: usize) -> usize { + todo!() +} + +macro_rules! gen { + () => { + (0..10).map(|_| do_something()); + }; +} + +fn main() { + // These should be raised + (0..10).map(|_| do_something()); + (0..10).map(|_foo| do_something()); + (0..=10).map(|_| do_something()); + (3..10).map(|_| do_something()); + (3..=10).map(|_| do_something()); + (0..10).map(|_| 3); + (0..10).map(|_| { + let x = 3; + x + 2 + }); + (0..10).map(|_| do_something()).collect::>(); + let upper = 4; + (0..upper).map(|_| do_something()); + let upper_fn = || 4; + (0..upper_fn()).map(|_| do_something()); + (0..=upper_fn()).map(|_| do_something()); + (2..upper_fn()).map(|_| do_something()); + (2..=upper_fn()).map(|_| do_something()); + (-3..9).map(|_| do_something()); + (9..3).map(|_| do_something()); + (9..=9).map(|_| do_something()); + (1..=1 << 4).map(|_| do_something()); + // These should not be raised + gen!(); + let lower = 2; + let lower_fn = || 2; + (lower..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled + (lower_fn()..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled + (lower_fn()..=upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled + (0..10).map(|x| do_something_interesting(x, 4)); // Actual map over range + "Foobar".chars().map(|_| do_something()); // Not a map over range + // i128::MAX == 340282366920938463463374607431768211455 + (0..=340282366920938463463374607431768211455).map(|_: u128| do_something()); // Can't be replaced due to overflow +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + (0..10).map(|_| do_something()); +} + +#[clippy::msrv = "1.28"] +fn msrv_1_28() { + (0..10).map(|_| do_something()); +} + +#[clippy::msrv = "1.81"] +fn msrv_1_82() { + (0..10).map(|_| 3); +} diff --git a/tests/ui/map_with_unused_argument_over_ranges.stderr b/tests/ui/map_with_unused_argument_over_ranges.stderr new file mode 100644 index 00000000000..0b56c6d9521 --- /dev/null +++ b/tests/ui/map_with_unused_argument_over_ranges.stderr @@ -0,0 +1,223 @@ +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:25:5 + | +LL | (0..10).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::map-with-unused-argument-over-ranges` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::map_with_unused_argument_over_ranges)]` +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..10).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(10); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:26:5 + | +LL | (0..10).map(|_foo| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..10).map(|_foo| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(10); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:27:5 + | +LL | (0..=10).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..=10).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(11); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:28:5 + | +LL | (3..10).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (3..10).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(7); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:29:5 + | +LL | (3..=10).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (3..=10).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(8); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:30:5 + | +LL | (0..10).map(|_| 3); + | ^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_n` + | +LL | std::iter::repeat_n(3, 10); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~ + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:31:5 + | +LL | / (0..10).map(|_| { +LL | | let x = 3; +LL | | x + 2 +LL | | }); + | |______^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL ~ std::iter::repeat_with(|| { +LL | let x = 3; +LL | x + 2 +LL ~ }).take(10); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:35:5 + | +LL | (0..10).map(|_| do_something()).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..10).map(|_| do_something()).collect::>(); +LL + std::iter::repeat_with(|| do_something()).take(10).collect::>(); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:37:5 + | +LL | (0..upper).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..upper).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(upper); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:39:5 + | +LL | (0..upper_fn()).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..upper_fn()).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(upper_fn()); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:40:5 + | +LL | (0..=upper_fn()).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..=upper_fn()).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(upper_fn() + 1); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:41:5 + | +LL | (2..upper_fn()).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (2..upper_fn()).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(upper_fn() - 2); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:42:5 + | +LL | (2..=upper_fn()).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (2..=upper_fn()).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(upper_fn() - 1); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:44:5 + | +LL | (9..3).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (9..3).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(0); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:45:5 + | +LL | (9..=9).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (9..=9).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(1); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:46:5 + | +LL | (1..=1 << 4).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (1..=1 << 4).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take((1 << 4) - 0); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:67:5 + | +LL | (0..10).map(|_| do_something()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat_with` and `take` + | +LL - (0..10).map(|_| do_something()); +LL + std::iter::repeat_with(|| do_something()).take(10); + | + +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges.rs:72:5 + | +LL | (0..10).map(|_| 3); + | ^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit range and use `repeat` and `take` + | +LL | std::iter::repeat(3).take(10); + | ~~~~~~~~~~~~~~~~~ ~ +++++++++ + +error: aborting due to 18 previous errors + diff --git a/tests/ui/missing_const_for_fn/could_be_const.fixed b/tests/ui/missing_const_for_fn/could_be_const.fixed index 7c882789511..754fe061c4a 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -2,7 +2,6 @@ #![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] #![feature(const_trait_impl, abi_vectorcall)] - use std::mem::transmute; struct Game { diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 48312d48ed3..460be0733e0 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -2,7 +2,6 @@ #![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] #![feature(const_trait_impl, abi_vectorcall)] - use std::mem::transmute; struct Game { diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index f28dec5c7f1..d553c522556 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -1,5 +1,5 @@ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:14:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:13:5 | LL | / pub fn new() -> Self { LL | | @@ -16,7 +16,7 @@ LL | pub const fn new() -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:20:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:19:5 | LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { LL | | @@ -30,7 +30,7 @@ LL | const fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:27:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:26:1 | LL | / fn one() -> i32 { LL | | @@ -44,7 +44,7 @@ LL | const fn one() -> i32 { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:33:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:32:1 | LL | / fn two() -> i32 { LL | | @@ -59,7 +59,7 @@ LL | const fn two() -> i32 { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:40:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:39:1 | LL | / fn string() -> String { LL | | @@ -73,7 +73,7 @@ LL | const fn string() -> String { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:46:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:45:1 | LL | / unsafe fn four() -> i32 { LL | | @@ -87,7 +87,7 @@ LL | const unsafe fn four() -> i32 { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:52:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:51:1 | LL | / fn generic(t: T) -> T { LL | | @@ -101,7 +101,7 @@ LL | const fn generic(t: T) -> T { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:61:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:60:1 | LL | / fn generic_arr(t: [T; 1]) -> T { LL | | @@ -115,7 +115,7 @@ LL | const fn generic_arr(t: [T; 1]) -> T { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:75:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:74:9 | LL | / pub fn b(self, a: &A) -> B { LL | | @@ -129,7 +129,7 @@ LL | pub const fn b(self, a: &A) -> B { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:85:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:84:5 | LL | / fn const_fn_stabilized_before_msrv(byte: u8) { LL | | @@ -143,7 +143,7 @@ LL | const fn const_fn_stabilized_before_msrv(byte: u8) { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:97:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:96:1 | LL | / fn msrv_1_46() -> i32 { LL | | @@ -157,7 +157,7 @@ LL | const fn msrv_1_46() -> i32 { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:117:1 + --> tests/ui/missing_const_for_fn/could_be_const.rs:116:1 | LL | fn d(this: D) {} | ^^^^^^^^^^^^^^^^ @@ -168,7 +168,7 @@ LL | const fn d(this: D) {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:125:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:124:9 | LL | / fn deref_ptr_can_be_const(self) -> usize { LL | | @@ -182,7 +182,7 @@ LL | const fn deref_ptr_can_be_const(self) -> usize { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:130:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:129:9 | LL | / fn deref_copied_val(self) -> usize { LL | | @@ -196,7 +196,7 @@ LL | const fn deref_copied_val(self) -> usize { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:141:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:140:5 | LL | / fn union_access_can_be_const() { LL | | @@ -211,7 +211,7 @@ LL | const fn union_access_can_be_const() { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:149:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:148:9 | LL | extern "C" fn c() {} | ^^^^^^^^^^^^^^^^^^^^ @@ -222,7 +222,7 @@ LL | const extern "C" fn c() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:153:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:152:9 | LL | extern fn implicit_c() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL | const extern fn implicit_c() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:170:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:169:9 | LL | / pub fn new(strings: Vec) -> Self { LL | | Self { strings } @@ -246,7 +246,7 @@ LL | pub const fn new(strings: Vec) -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:175:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:174:9 | LL | / pub fn empty() -> Self { LL | | Self { strings: Vec::new() } @@ -259,7 +259,7 @@ LL | pub const fn empty() -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:186:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:185:9 | LL | / pub fn new(text: String) -> Self { LL | | let vec = Vec::new(); @@ -273,7 +273,7 @@ LL | pub const fn new(text: String) -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:205:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:204:5 | LL | fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -284,7 +284,7 @@ LL | const fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:209:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:208:5 | LL | extern "C-unwind" fn c_unwind() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -295,7 +295,7 @@ LL | const extern "C-unwind" fn c_unwind() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:211:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:210:5 | LL | extern "system" fn system() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -306,7 +306,7 @@ LL | const extern "system" fn system() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:213:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:212:5 | LL | extern "system-unwind" fn system_unwind() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -317,7 +317,7 @@ LL | const extern "system-unwind" fn system_unwind() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:215:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:214:5 | LL | pub extern "vectorcall" fn std_call() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -328,7 +328,7 @@ LL | pub const extern "vectorcall" fn std_call() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:217:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:216:5 | LL | pub extern "vectorcall-unwind" fn std_call_unwind() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/missing_doc.rs b/tests/ui/missing_doc.rs index 9c936d7fa23..31bc4309582 100644 --- a/tests/ui/missing_doc.rs +++ b/tests/ui/missing_doc.rs @@ -116,6 +116,14 @@ fn main() {} with_span!(span pub static FOO_PM: u32 = 0;); with_span!(span pub const FOO2_PM: u32 = 0;); +// Don't lint unnamed constants +const _: () = (); + +fn issue13298() { + // Rustdoc doesn't generate documentation for items within other items like fns or consts + const MSG: &str = "Hello, world!"; +} + // issue #12197 // Undocumented field originated inside of spanned proc-macro attribute /// Some dox for struct. diff --git a/tests/ui/missing_doc.stderr b/tests/ui/missing_doc.stderr index ef0f96a5b71..133c76ac9d4 100644 --- a/tests/ui/missing_doc.stderr +++ b/tests/ui/missing_doc.stderr @@ -88,5 +88,14 @@ error: missing documentation for a function LL | fn also_undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: missing documentation for a function + --> tests/ui/missing_doc.rs:122:1 + | +LL | / fn issue13298() { +LL | | // Rustdoc doesn't generate documentation for items within other items like fns or consts +LL | | const MSG: &str = "Hello, world!"; +LL | | } + | |_^ + +error: aborting due to 14 previous errors diff --git a/tests/ui/needless_as_bytes.fixed b/tests/ui/needless_as_bytes.fixed new file mode 100644 index 00000000000..042342311fd --- /dev/null +++ b/tests/ui/needless_as_bytes.fixed @@ -0,0 +1,50 @@ +#![warn(clippy::needless_as_bytes)] +#![allow(clippy::const_is_empty)] + +struct S; + +impl S { + fn as_bytes(&self) -> &[u8] { + &[] + } +} + +fn main() { + if "some string".is_empty() { + //~^ needless_as_bytes + println!("len = {}", "some string".len()); + //~^ needless_as_bytes + } + + let s = String::from("yet another string"); + if s.is_empty() { + //~^ needless_as_bytes + println!("len = {}", s.len()); + //~^ needless_as_bytes + } + + // Do not lint + let _ = S.as_bytes().is_empty(); + let _ = S.as_bytes().len(); + let _ = (&String::new() as &dyn AsBytes).as_bytes().len(); + macro_rules! m { + (1) => { + "" + }; + (2) => { + "".as_bytes() + }; + } + m!(1).as_bytes().len(); + m!(2).len(); +} + +pub trait AsBytes { + fn as_bytes(&self) -> &[u8]; +} + +impl AsBytes for String { + fn as_bytes(&self) -> &[u8] { + &[] + } +} diff --git a/tests/ui/needless_as_bytes.rs b/tests/ui/needless_as_bytes.rs new file mode 100644 index 00000000000..c481e041e0a --- /dev/null +++ b/tests/ui/needless_as_bytes.rs @@ -0,0 +1,50 @@ +#![warn(clippy::needless_as_bytes)] +#![allow(clippy::const_is_empty)] + +struct S; + +impl S { + fn as_bytes(&self) -> &[u8] { + &[] + } +} + +fn main() { + if "some string".as_bytes().is_empty() { + //~^ needless_as_bytes + println!("len = {}", "some string".as_bytes().len()); + //~^ needless_as_bytes + } + + let s = String::from("yet another string"); + if s.as_bytes().is_empty() { + //~^ needless_as_bytes + println!("len = {}", s.as_bytes().len()); + //~^ needless_as_bytes + } + + // Do not lint + let _ = S.as_bytes().is_empty(); + let _ = S.as_bytes().len(); + let _ = (&String::new() as &dyn AsBytes).as_bytes().len(); + macro_rules! m { + (1) => { + "" + }; + (2) => { + "".as_bytes() + }; + } + m!(1).as_bytes().len(); + m!(2).len(); +} + +pub trait AsBytes { + fn as_bytes(&self) -> &[u8]; +} + +impl AsBytes for String { + fn as_bytes(&self) -> &[u8] { + &[] + } +} diff --git a/tests/ui/needless_as_bytes.stderr b/tests/ui/needless_as_bytes.stderr new file mode 100644 index 00000000000..3391238a142 --- /dev/null +++ b/tests/ui/needless_as_bytes.stderr @@ -0,0 +1,29 @@ +error: needless call to `as_bytes()` + --> tests/ui/needless_as_bytes.rs:13:8 + | +LL | if "some string".as_bytes().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `"some string".is_empty()` + | + = note: `-D clippy::needless-as-bytes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_as_bytes)]` + +error: needless call to `as_bytes()` + --> tests/ui/needless_as_bytes.rs:15:30 + | +LL | println!("len = {}", "some string".as_bytes().len()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `"some string".len()` + +error: needless call to `as_bytes()` + --> tests/ui/needless_as_bytes.rs:20:8 + | +LL | if s.as_bytes().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `s.is_empty()` + +error: needless call to `as_bytes()` + --> tests/ui/needless_as_bytes.rs:22:30 + | +LL | println!("len = {}", s.as_bytes().len()); + | ^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `s.len()` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/needless_continue.rs b/tests/ui/needless_continue.rs index c26a292c8cb..b6d8a8f61ae 100644 --- a/tests/ui/needless_continue.rs +++ b/tests/ui/needless_continue.rs @@ -151,3 +151,20 @@ fn bar() { } } } + +fn issue_13641() { + 'a: while std::hint::black_box(true) { + #[allow(clippy::never_loop)] + loop { + continue 'a; + } + } + + #[allow(clippy::never_loop)] + while std::hint::black_box(true) { + 'b: loop { + continue 'b; + //~^ ERROR: this `continue` expression is redundant + } + } +} diff --git a/tests/ui/needless_continue.stderr b/tests/ui/needless_continue.stderr index ec7c9ba39a7..0741ba69248 100644 --- a/tests/ui/needless_continue.stderr +++ b/tests/ui/needless_continue.stderr @@ -136,5 +136,13 @@ LL | | } println!("bar-5"); } -error: aborting due to 8 previous errors +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:166:13 + | +LL | continue 'b; + | ^^^^^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: aborting due to 9 previous errors diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index fee05926ce4..18adfff8beb 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -10,7 +10,7 @@ /// unimplemented!(); /// } /// ``` -/// +/// /// With an explicit return type it should lint too /// ```edition2015 /// fn main() -> () { @@ -18,7 +18,7 @@ /// unimplemented!(); /// } /// ``` -/// +/// /// This should, too. /// ```rust /// fn main() { @@ -26,7 +26,7 @@ /// unimplemented!(); /// } /// ``` -/// +/// /// This one too. /// ```no_run /// // the fn is not always the first line diff --git a/tests/ui/no_lints.rs b/tests/ui/no_lints.rs index a8467bb6ef7..77c466c4a55 100644 --- a/tests/ui/no_lints.rs +++ b/tests/ui/no_lints.rs @@ -1,3 +1,3 @@ #![deny(clippy::all)] -fn main() {} \ No newline at end of file +fn main() {} diff --git a/tests/ui/no_mangle_with_rust_abi.rs b/tests/ui/no_mangle_with_rust_abi.rs index 8c1ea81d2ac..8be149d1863 100644 --- a/tests/ui/no_mangle_with_rust_abi.rs +++ b/tests/ui/no_mangle_with_rust_abi.rs @@ -2,30 +2,30 @@ #![allow(unused)] #![warn(clippy::no_mangle_with_rust_abi)] -#[no_mangle] +#[unsafe(no_mangle)] fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} -//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI +//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI //~| NOTE: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings` -#[no_mangle] +#[unsafe(no_mangle)] pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} -//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI +//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI /// # Safety /// This function shouldn't be called unless the horsemen are ready -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} -//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI +//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI /// # Safety /// This function shouldn't be called unless the horsemen are ready -#[no_mangle] +#[unsafe(no_mangle)] unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} -//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI +//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI -#[no_mangle] +#[unsafe(no_mangle)] fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( - //~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI + //~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI arg_one: u32, arg_two: usize, ) -> u32 { @@ -33,13 +33,13 @@ fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lin } // Must not run on functions that explicitly opt in to using the Rust ABI with `extern "Rust"` -#[no_mangle] +#[unsafe(no_mangle)] #[rustfmt::skip] extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {} fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {} -#[no_mangle] +#[unsafe(no_mangle)] extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {} extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {} @@ -48,6 +48,11 @@ extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {} fn c_abi_in_block(arg_one: u32, arg_two: usize); } +mod r#fn { + #[unsafe(no_mangle)] + pub(in super::r#fn) fn with_some_fn_around() {} +} + fn main() { // test code goes here } diff --git a/tests/ui/no_mangle_with_rust_abi.stderr b/tests/ui/no_mangle_with_rust_abi.stderr index 27b8b39a21a..a00ebe5e1ac 100644 --- a/tests/ui/no_mangle_with_rust_abi.stderr +++ b/tests/ui/no_mangle_with_rust_abi.stderr @@ -1,4 +1,4 @@ -error: `#[no_mangle]` set on a function with the default (`Rust`) ABI +error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI --> tests/ui/no_mangle_with_rust_abi.rs:6:1 | LL | fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} @@ -15,7 +15,7 @@ help: or explicitly set the default LL | extern "Rust" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} | +++++++++++++ -error: `#[no_mangle]` set on a function with the default (`Rust`) ABI +error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI --> tests/ui/no_mangle_with_rust_abi.rs:11:1 | LL | pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} @@ -30,7 +30,7 @@ help: or explicitly set the default LL | pub extern "Rust" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} | +++++++++++++ -error: `#[no_mangle]` set on a function with the default (`Rust`) ABI +error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI --> tests/ui/no_mangle_with_rust_abi.rs:17:1 | LL | pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} @@ -45,7 +45,7 @@ help: or explicitly set the default LL | pub unsafe extern "Rust" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} | +++++++++++++ -error: `#[no_mangle]` set on a function with the default (`Rust`) ABI +error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI --> tests/ui/no_mangle_with_rust_abi.rs:23:1 | LL | unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} @@ -60,7 +60,7 @@ help: or explicitly set the default LL | unsafe extern "Rust" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} | +++++++++++++ -error: `#[no_mangle]` set on a function with the default (`Rust`) ABI +error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI --> tests/ui/no_mangle_with_rust_abi.rs:27:1 | LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( @@ -79,5 +79,20 @@ help: or explicitly set the default LL | extern "Rust" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( | +++++++++++++ -error: aborting due to 5 previous errors +error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI + --> tests/ui/no_mangle_with_rust_abi.rs:53:5 + | +LL | pub(in super::r#fn) fn with_some_fn_around() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: set an ABI + | +LL | pub(in super::r#fn) extern "C" fn with_some_fn_around() {} + | ++++++++++ +help: or explicitly set the default + | +LL | pub(in super::r#fn) extern "Rust" fn with_some_fn_around() {} + | +++++++++++++ + +error: aborting due to 6 previous errors diff --git a/tests/ui/no_mangle_with_rust_abi_2021.rs b/tests/ui/no_mangle_with_rust_abi_2021.rs new file mode 100644 index 00000000000..c7c97335348 --- /dev/null +++ b/tests/ui/no_mangle_with_rust_abi_2021.rs @@ -0,0 +1,57 @@ +//@edition:2021 +// +// Edition 2024 requires the use of #[unsafe(no_mangle)] + +//@no-rustfix: overlapping suggestions +#![allow(unused)] +#![warn(clippy::no_mangle_with_rust_abi)] + +#[no_mangle] +fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} +//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI +//~| NOTE: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings` + +#[no_mangle] +pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} +//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI + +/// # Safety +/// This function shouldn't be called unless the horsemen are ready +#[no_mangle] +pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} +//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI + +/// # Safety +/// This function shouldn't be called unless the horsemen are ready +#[no_mangle] +unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} +//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI + +#[no_mangle] +fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( + //~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI + arg_one: u32, + arg_two: usize, +) -> u32 { + 0 +} + +// Must not run on functions that explicitly opt in to using the Rust ABI with `extern "Rust"` +#[no_mangle] +#[rustfmt::skip] +extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {} + +fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {} + +#[no_mangle] +extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {} + +extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {} + +extern "C" { + fn c_abi_in_block(arg_one: u32, arg_two: usize); +} + +fn main() { + // test code goes here +} diff --git a/tests/ui/no_mangle_with_rust_abi_2021.stderr b/tests/ui/no_mangle_with_rust_abi_2021.stderr new file mode 100644 index 00000000000..15075be72d0 --- /dev/null +++ b/tests/ui/no_mangle_with_rust_abi_2021.stderr @@ -0,0 +1,83 @@ +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> tests/ui/no_mangle_with_rust_abi_2021.rs:10:1 + | +LL | fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::no_mangle_with_rust_abi)]` +help: set an ABI + | +LL | extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} + | ++++++++++ +help: or explicitly set the default + | +LL | extern "Rust" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} + | +++++++++++++ + +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> tests/ui/no_mangle_with_rust_abi_2021.rs:15:1 + | +LL | pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: set an ABI + | +LL | pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} + | ++++++++++ +help: or explicitly set the default + | +LL | pub extern "Rust" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} + | +++++++++++++ + +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> tests/ui/no_mangle_with_rust_abi_2021.rs:21:1 + | +LL | pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: set an ABI + | +LL | pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} + | ++++++++++ +help: or explicitly set the default + | +LL | pub unsafe extern "Rust" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} + | +++++++++++++ + +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> tests/ui/no_mangle_with_rust_abi_2021.rs:27:1 + | +LL | unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: set an ABI + | +LL | unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} + | ++++++++++ +help: or explicitly set the default + | +LL | unsafe extern "Rust" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} + | +++++++++++++ + +error: `#[no_mangle]` set on a function with the default (`Rust`) ABI + --> tests/ui/no_mangle_with_rust_abi_2021.rs:31:1 + | +LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( +LL | | +LL | | arg_one: u32, +LL | | arg_two: usize, +LL | | ) -> u32 { + | |________^ + | +help: set an ABI + | +LL | extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( + | ++++++++++ +help: or explicitly set the default + | +LL | extern "Rust" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( + | +++++++++++++ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/nonminimal_bool.stderr b/tests/ui/nonminimal_bool.stderr index eafffdaf8a6..578f918f013 100644 --- a/tests/ui/nonminimal_bool.stderr +++ b/tests/ui/nonminimal_bool.stderr @@ -118,25 +118,25 @@ error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:161:8 | LL | if !(12 == a) {} - | ^^^^^^^^^^ help: try: `12 != a` + | ^^^^^^^^^^ help: try: `(12 != a)` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:162:8 | LL | if !(a == 12) {} - | ^^^^^^^^^^ help: try: `a != 12` + | ^^^^^^^^^^ help: try: `(a != 12)` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:163:8 | LL | if !(12 != a) {} - | ^^^^^^^^^^ help: try: `12 == a` + | ^^^^^^^^^^ help: try: `(12 == a)` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:164:8 | LL | if !(a != 12) {} - | ^^^^^^^^^^ help: try: `a == 12` + | ^^^^^^^^^^ help: try: `(a == 12)` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:168:8 diff --git a/tests/ui/nonminimal_bool_methods.fixed b/tests/ui/nonminimal_bool_methods.fixed index a23310c1ad9..65ccaaca891 100644 --- a/tests/ui/nonminimal_bool_methods.fixed +++ b/tests/ui/nonminimal_bool_methods.fixed @@ -110,9 +110,33 @@ fn dont_warn_for_negated_partial_ord_comparison() { fn issue_12625() { let a = 0; let b = 0; - if (a as u64) < b {} //~ ERROR: this boolean expression can be simplified - if (a as u64) < b {} //~ ERROR: this boolean expression can be simplified - if a as u64 > b {} //~ ERROR: this boolean expression can be simplified + if ((a as u64) < b) {} //~ ERROR: this boolean expression can be simplified + if ((a as u64) < b) {} //~ ERROR: this boolean expression can be simplified + if (a as u64 > b) {} //~ ERROR: this boolean expression can be simplified +} + +fn issue_12761() { + let a = 0; + let b = 0; + let c = 0; + if (a < b) as i32 == c {} //~ ERROR: this boolean expression can be simplified + if (a < b) | (a > c) {} //~ ERROR: this boolean expression can be simplified + let opt: Option = Some(1); + let res: Result = Ok(1); + if res.is_err() as i32 == c {} //~ ERROR: this boolean expression can be simplified + if res.is_err() | opt.is_some() {} //~ ERROR: this boolean expression can be simplified + + fn a(a: bool) -> bool { + (4 <= 3).b() //~ ERROR: this boolean expression can be simplified + } + + trait B { + fn b(&self) -> bool { + true + } + } + + impl B for bool {} } fn issue_13436() { diff --git a/tests/ui/nonminimal_bool_methods.rs b/tests/ui/nonminimal_bool_methods.rs index 6c844373af7..06db3a1d4a5 100644 --- a/tests/ui/nonminimal_bool_methods.rs +++ b/tests/ui/nonminimal_bool_methods.rs @@ -115,6 +115,30 @@ fn issue_12625() { if !(a as u64 <= b) {} //~ ERROR: this boolean expression can be simplified } +fn issue_12761() { + let a = 0; + let b = 0; + let c = 0; + if !(a >= b) as i32 == c {} //~ ERROR: this boolean expression can be simplified + if !(a >= b) | !(a <= c) {} //~ ERROR: this boolean expression can be simplified + let opt: Option = Some(1); + let res: Result = Ok(1); + if !res.is_ok() as i32 == c {} //~ ERROR: this boolean expression can be simplified + if !res.is_ok() | !opt.is_none() {} //~ ERROR: this boolean expression can be simplified + + fn a(a: bool) -> bool { + (!(4 > 3)).b() //~ ERROR: this boolean expression can be simplified + } + + trait B { + fn b(&self) -> bool { + true + } + } + + impl B for bool {} +} + fn issue_13436() { fn not_zero(x: i32) -> bool { x != 0 diff --git a/tests/ui/nonminimal_bool_methods.stderr b/tests/ui/nonminimal_bool_methods.stderr index 52803e828ae..66c50f9ff1e 100644 --- a/tests/ui/nonminimal_bool_methods.stderr +++ b/tests/ui/nonminimal_bool_methods.stderr @@ -83,127 +83,169 @@ error: this boolean expression can be simplified --> tests/ui/nonminimal_bool_methods.rs:113:8 | LL | if !(a as u64 >= b) {} - | ^^^^^^^^^^^^^^^^ help: try: `(a as u64) < b` + | ^^^^^^^^^^^^^^^^ help: try: `((a as u64) < b)` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool_methods.rs:114:8 | LL | if !((a as u64) >= b) {} - | ^^^^^^^^^^^^^^^^^^ help: try: `(a as u64) < b` + | ^^^^^^^^^^^^^^^^^^ help: try: `((a as u64) < b)` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool_methods.rs:115:8 | LL | if !(a as u64 <= b) {} - | ^^^^^^^^^^^^^^^^ help: try: `a as u64 > b` + | ^^^^^^^^^^^^^^^^ help: try: `(a as u64 > b)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:131:9 + --> tests/ui/nonminimal_bool_methods.rs:122:8 + | +LL | if !(a >= b) as i32 == c {} + | ^^^^^^^^^ help: try: `(a < b)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:123:8 + | +LL | if !(a >= b) | !(a <= c) {} + | ^^^^^^^^^ help: try: `(a < b)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:123:20 + | +LL | if !(a >= b) | !(a <= c) {} + | ^^^^^^^^^ help: try: `(a > c)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:126:8 + | +LL | if !res.is_ok() as i32 == c {} + | ^^^^^^^^^^^^ help: try: `res.is_err()` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:127:8 + | +LL | if !res.is_ok() | !opt.is_none() {} + | ^^^^^^^^^^^^ help: try: `res.is_err()` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:127:23 + | +LL | if !res.is_ok() | !opt.is_none() {} + | ^^^^^^^^^^^^^^ help: try: `opt.is_some()` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:130:9 + | +LL | (!(4 > 3)).b() + | ^^^^^^^^^^ help: try: `(4 <= 3)` + +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:155:9 | LL | _ = !opt.is_some_and(|x| x < 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x >= 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:132:9 + --> tests/ui/nonminimal_bool_methods.rs:156:9 | LL | _ = !opt.is_some_and(|x| x <= 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x > 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:133:9 + --> tests/ui/nonminimal_bool_methods.rs:157:9 | LL | _ = !opt.is_some_and(|x| x > 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x <= 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:134:9 + --> tests/ui/nonminimal_bool_methods.rs:158:9 | LL | _ = !opt.is_some_and(|x| x >= 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x < 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:135:9 + --> tests/ui/nonminimal_bool_methods.rs:159:9 | LL | _ = !opt.is_some_and(|x| x == 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x != 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:136:9 + --> tests/ui/nonminimal_bool_methods.rs:160:9 | LL | _ = !opt.is_some_and(|x| x != 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x == 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:145:9 + --> tests/ui/nonminimal_bool_methods.rs:169:9 | LL | _ = !opt.is_none_or(|x| x < 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x >= 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:146:9 + --> tests/ui/nonminimal_bool_methods.rs:170:9 | LL | _ = !opt.is_none_or(|x| x <= 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x > 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:147:9 + --> tests/ui/nonminimal_bool_methods.rs:171:9 | LL | _ = !opt.is_none_or(|x| x > 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x <= 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:148:9 + --> tests/ui/nonminimal_bool_methods.rs:172:9 | LL | _ = !opt.is_none_or(|x| x >= 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x < 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:149:9 + --> tests/ui/nonminimal_bool_methods.rs:173:9 | LL | _ = !opt.is_none_or(|x| x == 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x != 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:150:9 + --> tests/ui/nonminimal_bool_methods.rs:174:9 | LL | _ = !opt.is_none_or(|x| x != 1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x == 1000)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:157:9 + --> tests/ui/nonminimal_bool_methods.rs:181:9 | LL | _ = !opt.is_some_and(|x| !x); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:161:9 + --> tests/ui/nonminimal_bool_methods.rs:185:9 | LL | _ = !opt.is_none_or(|x| !x); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:168:9 + --> tests/ui/nonminimal_bool_methods.rs:192:9 | LL | _ = !opt.is_some_and(|x| x.is_ok()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x.is_err())` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:169:9 + --> tests/ui/nonminimal_bool_methods.rs:193:9 | LL | _ = !opt.is_some_and(|x| x.is_err()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x.is_ok())` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:170:9 + --> tests/ui/nonminimal_bool_methods.rs:194:9 | LL | _ = !opt.is_none_or(|x| x.is_ok()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x.is_err())` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:171:9 + --> tests/ui/nonminimal_bool_methods.rs:195:9 | LL | _ = !opt.is_none_or(|x| x.is_err()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x.is_ok())` -error: aborting due to 34 previous errors +error: aborting due to 41 previous errors diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index e03df1658ee..2ac59718786 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -54,7 +54,7 @@ #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] -#![allow(temporary_cstring_as_ptr)] +#![allow(dangling_pointers_from_temporaries)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index b906079d7df..a5ae727b7ee 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,17 +1,11 @@ -error: lint `temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:57:10 - | -LL | #![allow(temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` - error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` --> tests/ui/rename.rs:63:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` + = 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:64:9 @@ -403,5 +397,5 @@ error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_e LL | #![warn(clippy::reverse_range_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` -error: aborting due to 67 previous errors +error: aborting due to 66 previous errors diff --git a/tests/ui/repeat_vec_with_capacity.fixed b/tests/ui/repeat_vec_with_capacity.fixed index 2afe2f43325..f72b61b5f6c 100644 --- a/tests/ui/repeat_vec_with_capacity.fixed +++ b/tests/ui/repeat_vec_with_capacity.fixed @@ -1,3 +1,4 @@ +#![allow(clippy::map_with_unused_argument_over_ranges)] #![warn(clippy::repeat_vec_with_capacity)] fn main() { diff --git a/tests/ui/repeat_vec_with_capacity.rs b/tests/ui/repeat_vec_with_capacity.rs index 659f2a3953d..c0cc81f7843 100644 --- a/tests/ui/repeat_vec_with_capacity.rs +++ b/tests/ui/repeat_vec_with_capacity.rs @@ -1,3 +1,4 @@ +#![allow(clippy::map_with_unused_argument_over_ranges)] #![warn(clippy::repeat_vec_with_capacity)] fn main() { diff --git a/tests/ui/repeat_vec_with_capacity.stderr b/tests/ui/repeat_vec_with_capacity.stderr index cec9c6ea84a..43027c9cb89 100644 --- a/tests/ui/repeat_vec_with_capacity.stderr +++ b/tests/ui/repeat_vec_with_capacity.stderr @@ -1,5 +1,5 @@ error: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity - --> tests/ui/repeat_vec_with_capacity.rs:5:9 + --> tests/ui/repeat_vec_with_capacity.rs:6:9 | LL | vec![Vec::<()>::with_capacity(42); 123]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL | (0..123).map(|_| Vec::<()>::with_capacity(42)).collect::>(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity - --> tests/ui/repeat_vec_with_capacity.rs:11:9 + --> tests/ui/repeat_vec_with_capacity.rs:12:9 | LL | vec![Vec::<()>::with_capacity(42); n]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | (0..n).map(|_| Vec::<()>::with_capacity(42)).collect::>(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity - --> tests/ui/repeat_vec_with_capacity.rs:26:9 + --> tests/ui/repeat_vec_with_capacity.rs:27:9 | LL | std::iter::repeat(Vec::<()>::with_capacity(42)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/semicolon_outside_block.fixed b/tests/ui/semicolon_outside_block.fixed index 148e112e0bc..ac7e86631ca 100644 --- a/tests/ui/semicolon_outside_block.fixed +++ b/tests/ui/semicolon_outside_block.fixed @@ -80,5 +80,13 @@ fn main() { { unit_fn_block(); }; + unsafe { + std::arch::asm!("") + }; + + { + line!() + }; + unit_fn_block() } diff --git a/tests/ui/semicolon_outside_block.rs b/tests/ui/semicolon_outside_block.rs index c767201469a..68f25339e32 100644 --- a/tests/ui/semicolon_outside_block.rs +++ b/tests/ui/semicolon_outside_block.rs @@ -80,5 +80,13 @@ fn main() { { unit_fn_block(); }; + unsafe { + std::arch::asm!(""); + } + + { + line!(); + } + unit_fn_block() } diff --git a/tests/ui/semicolon_outside_block.stderr b/tests/ui/semicolon_outside_block.stderr index 68b44c8f980..ff8c00048f6 100644 --- a/tests/ui/semicolon_outside_block.stderr +++ b/tests/ui/semicolon_outside_block.stderr @@ -51,5 +51,33 @@ LL - { m!(()); } LL + { m!(()) }; | -error: aborting due to 4 previous errors +error: consider moving the `;` outside the block for consistent formatting + --> tests/ui/semicolon_outside_block.rs:83:5 + | +LL | / unsafe { +LL | | std::arch::asm!(""); +LL | | } + | |_____^ + | +help: put the `;` here + | +LL ~ std::arch::asm!("") +LL ~ }; + | + +error: consider moving the `;` outside the block for consistent formatting + --> tests/ui/semicolon_outside_block.rs:87:5 + | +LL | / { +LL | | line!(); +LL | | } + | |_____^ + | +help: put the `;` here + | +LL ~ line!() +LL ~ }; + | + +error: aborting due to 6 previous errors diff --git a/tests/ui/suspicious_map.rs b/tests/ui/suspicious_map.rs index d4247fcd926..d4a52cb110f 100644 --- a/tests/ui/suspicious_map.rs +++ b/tests/ui/suspicious_map.rs @@ -1,3 +1,4 @@ +#![allow(clippy::map_with_unused_argument_over_ranges)] #![warn(clippy::suspicious_map)] fn main() { diff --git a/tests/ui/suspicious_map.stderr b/tests/ui/suspicious_map.stderr index 2a5e2bf2a34..769adebaede 100644 --- a/tests/ui/suspicious_map.stderr +++ b/tests/ui/suspicious_map.stderr @@ -1,5 +1,5 @@ error: this call to `map()` won't have an effect on the call to `count()` - --> tests/ui/suspicious_map.rs:4:13 + --> tests/ui/suspicious_map.rs:5:13 | LL | let _ = (0..3).map(|x| x + 2).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = (0..3).map(|x| x + 2).count(); = help: to override `-D warnings` add `#[allow(clippy::suspicious_map)]` error: this call to `map()` won't have an effect on the call to `count()` - --> tests/ui/suspicious_map.rs:8:13 + --> tests/ui/suspicious_map.rs:9:13 | LL | let _ = (0..3).map(f).count(); | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/to_string_in_format_args_incremental.fixed b/tests/ui/to_string_in_format_args_incremental.fixed new file mode 100644 index 00000000000..1f789579636 --- /dev/null +++ b/tests/ui/to_string_in_format_args_incremental.fixed @@ -0,0 +1,8 @@ +//@compile-flags: -C incremental=target/debug/test/incr + +// see https://github.com/rust-lang/rust-clippy/issues/10969 + +fn main() { + let s = "Hello, world!"; + println!("{}", s); +} diff --git a/tests/ui/to_string_in_format_args_incremental.rs b/tests/ui/to_string_in_format_args_incremental.rs new file mode 100644 index 00000000000..514febe8c92 --- /dev/null +++ b/tests/ui/to_string_in_format_args_incremental.rs @@ -0,0 +1,8 @@ +//@compile-flags: -C incremental=target/debug/test/incr + +// see https://github.com/rust-lang/rust-clippy/issues/10969 + +fn main() { + let s = "Hello, world!"; + println!("{}", s.to_string()); +} diff --git a/tests/ui/to_string_in_format_args_incremental.stderr b/tests/ui/to_string_in_format_args_incremental.stderr new file mode 100644 index 00000000000..535dd21ea58 --- /dev/null +++ b/tests/ui/to_string_in_format_args_incremental.stderr @@ -0,0 +1,11 @@ +error: `to_string` applied to a type that implements `Display` in `println!` args + --> tests/ui/to_string_in_format_args_incremental.rs:7:21 + | +LL | println!("{}", s.to_string()); + | ^^^^^^^^^^^^ help: remove this + | + = note: `-D clippy::to-string-in-format-args` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::to_string_in_format_args)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/unnecessary_filter_map.rs b/tests/ui/unnecessary_filter_map.rs index 1e0d7d12965..8cf102ab0a5 100644 --- a/tests/ui/unnecessary_filter_map.rs +++ b/tests/ui/unnecessary_filter_map.rs @@ -1,26 +1,31 @@ +//@no-rustfix #![allow(dead_code)] fn main() { let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); - //~^ ERROR: this `.filter_map` can be written more simply using `.filter` + //~^ ERROR: this `.filter_map` can be written more simply //~| NOTE: `-D clippy::unnecessary-filter-map` implied by `-D warnings` let _ = (0..4).filter_map(|x| { - //~^ ERROR: this `.filter_map` can be written more simply using `.filter` + //~^ ERROR: this `.filter_map` can be written more simply if x > 1 { return Some(x); }; None }); let _ = (0..4).filter_map(|x| match x { - //~^ ERROR: this `.filter_map` can be written more simply using `.filter` + //~^ ERROR: this `.filter_map` can be written more simply 0 | 1 => None, _ => Some(x), }); let _ = (0..4).filter_map(|x| Some(x + 1)); - //~^ ERROR: this `.filter_map` can be written more simply using `.map` + //~^ ERROR: this `.filter_map` can be written more simply let _ = (0..4).filter_map(i32::checked_abs); + + let _ = (0..4).filter_map(Some); + + let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); } fn filter_map_none_changes_item_type() -> impl Iterator { diff --git a/tests/ui/unnecessary_filter_map.stderr b/tests/ui/unnecessary_filter_map.stderr index f32d444ba9e..b21589c5f84 100644 --- a/tests/ui/unnecessary_filter_map.stderr +++ b/tests/ui/unnecessary_filter_map.stderr @@ -1,14 +1,14 @@ -error: this `.filter_map` can be written more simply using `.filter` - --> tests/ui/unnecessary_filter_map.rs:4:13 +error: this `.filter_map` can be written more simply + --> tests/ui/unnecessary_filter_map.rs:5:13 | LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `filter` | = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_filter_map)]` -error: this `.filter_map` can be written more simply using `.filter` - --> tests/ui/unnecessary_filter_map.rs:7:13 +error: this `.filter_map` can be written more simply + --> tests/ui/unnecessary_filter_map.rs:8:13 | LL | let _ = (0..4).filter_map(|x| { | _____________^ @@ -18,10 +18,10 @@ LL | | return Some(x); LL | | }; LL | | None LL | | }); - | |______^ + | |______^ help: try instead: `filter` -error: this `.filter_map` can be written more simply using `.filter` - --> tests/ui/unnecessary_filter_map.rs:14:13 +error: this `.filter_map` can be written more simply + --> tests/ui/unnecessary_filter_map.rs:15:13 | LL | let _ = (0..4).filter_map(|x| match x { | _____________^ @@ -29,19 +29,40 @@ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), LL | | }); - | |______^ + | |______^ help: try instead: `filter` -error: this `.filter_map` can be written more simply using `.map` - --> tests/ui/unnecessary_filter_map.rs:20:13 +error: this `.filter_map` can be written more simply + --> tests/ui/unnecessary_filter_map.rs:21:13 | LL | let _ = (0..4).filter_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map` -error: this `.filter_map` can be written more simply using `.filter` - --> tests/ui/unnecessary_filter_map.rs:160:14 +error: redundant closure + --> tests/ui/unnecessary_filter_map.rs:28:57 + | +LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); + | ^^^^^^^^^^^ help: replace the closure with the function itself: `Some` + | + = note: `-D clippy::redundant-closure` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` + +error: filter_map is unnecessary + --> tests/ui/unnecessary_filter_map.rs:28:61 + | +LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); + | ^^^^ help: try removing the filter_map + +error: this `.filter_map` can be written more simply + --> tests/ui/unnecessary_filter_map.rs:28:13 + | +LL | let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map` + +error: this `.filter_map` can be written more simply + --> tests/ui/unnecessary_filter_map.rs:165:14 | LL | let _x = std::iter::once(1).filter_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `filter` -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/unnecessary_find_map.rs b/tests/ui/unnecessary_find_map.rs index 9972b68092a..c357d853248 100644 --- a/tests/ui/unnecessary_find_map.rs +++ b/tests/ui/unnecessary_find_map.rs @@ -1,24 +1,25 @@ +//@no-rustfix #![allow(dead_code)] fn main() { let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None }); - //~^ ERROR: this `.find_map` can be written more simply using `.find` + //~^ ERROR: this `.find_map` can be written more simply //~| NOTE: `-D clippy::unnecessary-find-map` implied by `-D warnings` let _ = (0..4).find_map(|x| { - //~^ ERROR: this `.find_map` can be written more simply using `.find` + //~^ ERROR: this `.find_map` can be written more simply if x > 1 { return Some(x); }; None }); let _ = (0..4).find_map(|x| match x { - //~^ ERROR: this `.find_map` can be written more simply using `.find` + //~^ ERROR: this `.find_map` can be written more simply 0 | 1 => None, _ => Some(x), }); let _ = (0..4).find_map(|x| Some(x + 1)); - //~^ ERROR: this `.find_map` can be written more simply using `.map(..).next()` + //~^ ERROR: this `.find_map` can be written more simply let _ = (0..4).find_map(i32::checked_abs); } diff --git a/tests/ui/unnecessary_find_map.stderr b/tests/ui/unnecessary_find_map.stderr index bb939a99214..98a6c3d164a 100644 --- a/tests/ui/unnecessary_find_map.stderr +++ b/tests/ui/unnecessary_find_map.stderr @@ -1,14 +1,14 @@ -error: this `.find_map` can be written more simply using `.find` - --> tests/ui/unnecessary_find_map.rs:4:13 +error: this `.find_map` can be written more simply + --> tests/ui/unnecessary_find_map.rs:5:13 | LL | let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `find` | = note: `-D clippy::unnecessary-find-map` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_find_map)]` -error: this `.find_map` can be written more simply using `.find` - --> tests/ui/unnecessary_find_map.rs:7:13 +error: this `.find_map` can be written more simply + --> tests/ui/unnecessary_find_map.rs:8:13 | LL | let _ = (0..4).find_map(|x| { | _____________^ @@ -18,10 +18,10 @@ LL | | return Some(x); LL | | }; LL | | None LL | | }); - | |______^ + | |______^ help: try instead: `find` -error: this `.find_map` can be written more simply using `.find` - --> tests/ui/unnecessary_find_map.rs:14:13 +error: this `.find_map` can be written more simply + --> tests/ui/unnecessary_find_map.rs:15:13 | LL | let _ = (0..4).find_map(|x| match x { | _____________^ @@ -29,19 +29,19 @@ LL | | LL | | 0 | 1 => None, LL | | _ => Some(x), LL | | }); - | |______^ + | |______^ help: try instead: `find` -error: this `.find_map` can be written more simply using `.map(..).next()` - --> tests/ui/unnecessary_find_map.rs:20:13 +error: this `.find_map` can be written more simply + --> tests/ui/unnecessary_find_map.rs:21:13 | LL | let _ = (0..4).find_map(|x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map(..).next()` -error: this `.find_map` can be written more simply using `.find` - --> tests/ui/unnecessary_find_map.rs:32:14 +error: this `.find_map` can be written more simply + --> tests/ui/unnecessary_find_map.rs:33:14 | LL | let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(n)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `find` error: aborting due to 5 previous errors diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index f5b200d5ffe..175c4ca7689 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -277,4 +277,18 @@ fn allow_works(mut f: F) { f.read(&mut data).unwrap(); } +struct Reader {} + +impl Read for Reader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + todo!() + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + // We shouldn't recommend using Read::read_exact inside Read::read_exact! + self.read(buf).unwrap(); + Ok(()) + } +} + fn main() {} diff --git a/triagebot.toml b/triagebot.toml index cd9641eedd8..eadfd7107c7 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -21,13 +21,11 @@ new_pr = true contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", - "giraffate", ] [assign.owners] "/.github" = ["@flip1995"] "/book" = ["@flip1995"] -"/util/gh-pages" = ["@xFrednet"] "*" = [ "@Manishearth", "@llogiq", diff --git a/util/gh-pages/index_template.html b/util/gh-pages/index_template.html index 2412f0fd181..deb0ef0b499 100644 --- a/util/gh-pages/index_template.html +++ b/util/gh-pages/index_template.html @@ -52,12 +52,12 @@ Otherwise, have a great day =^.^= {# #}
{# #} -
{# #} +