diff --git a/CHANGELOG.md b/CHANGELOG.md index e2cde09776f..659e8aebcd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4383,6 +4383,7 @@ Released 2018-09-13 [`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice [`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain [`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes +[`extra_unused_type_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_type_parameters [`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from [`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default [`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file diff --git a/Cargo.toml b/Cargo.toml index 2cfb47dd758..70d1268090f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ filetime = "0.2" rustc-workspace-hack = "1.0" # UI test dependencies +clap = { version = "4.1.4", features = ["derive"] } clippy_utils = { path = "clippy_utils" } derive-new = "0.5" if_chain = "1.0" diff --git a/README.md b/README.md index ab44db69483..95f6d2cc45c 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 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 600 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 23867df8efe..df4a1f2702e 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 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 600 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/lint_configuration.md b/book/src/lint_configuration.md index f79dbb50ff4..32e8e218c40 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -43,6 +43,7 @@ Please use that command to update the file and do not edit it by hand. | [allowed-scripts](#allowed-scripts) | `["Latin"]` | | [enable-raw-pointer-heuristic-for-send](#enable-raw-pointer-heuristic-for-send) | `true` | | [max-suggested-slice-pattern-length](#max-suggested-slice-pattern-length) | `3` | +| [await-holding-invalid-types](#await-holding-invalid-types) | `[]` | | [max-include-file-size](#max-include-file-size) | `1000000` | | [allow-expect-in-tests](#allow-expect-in-tests) | `false` | | [allow-unwrap-in-tests](#allow-unwrap-in-tests) | `false` | @@ -167,6 +168,17 @@ The minimum rust version that the project supports * [manual_clamp](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) * [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) * [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) +* [collapsible_str_replace](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace) +* [seek_from_current](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) +* [seek_rewind](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) +* [unnecessary_lazy_evaluations](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) +* [transmute_ptr_to_ref](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) +* [almost_complete_range](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range) +* [needless_borrow](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) +* [derivable_impls](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls) +* [manual_is_ascii_check](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) +* [manual_rem_euclid](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) +* [manual_retain](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) ### cognitive-complexity-threshold @@ -279,7 +291,7 @@ The minimum size (in bytes) to consider a type for passing by reference instead **Default Value:** `256` (`u64`) -* [large_type_pass_by_move](https://rust-lang.github.io/rust-clippy/master/index.html#large_type_pass_by_move) +* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) ### too-many-lines-threshold @@ -442,6 +454,14 @@ For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. * [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) +### await-holding-invalid-types + + +**Default Value:** `[]` (`Vec`) + +* [await_holding_invalid_type](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type) + + ### max-include-file-size The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes @@ -497,7 +517,7 @@ for the generic parameters for determining interior mutability **Default Value:** `["bytes::Bytes"]` (`Vec`) -* [mutable_key](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key) +* [mutable_key_type](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) ### allow-mixed-uninlined-format-args @@ -509,7 +529,7 @@ Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar) ### suppress-restriction-lint-in-const -In same +Whether to suppress a restriction lint in constant code. In same cases the restructured operation might not be unavoidable, as the suggested counterparts are unavailable in constant code. This configuration will cause restriction lints to trigger even diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 510c7e852af..c3f8a782d27 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] aho-corasick = "0.7" -clap = "3.2" +clap = "4.1.4" indoc = "1.0" itertools = "0.10.1" opener = "0.5" diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index d3e03669204..e2457e5a8a5 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -2,7 +2,7 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue}; +use clap::{Arg, ArgAction, ArgMatches, Command}; use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints}; use indoc::indoc; @@ -11,22 +11,22 @@ fn main() { match matches.subcommand() { Some(("bless", matches)) => { - bless::bless(matches.contains_id("ignore-timestamp")); + bless::bless(matches.get_flag("ignore-timestamp")); }, Some(("dogfood", matches)) => { dogfood::dogfood( - matches.contains_id("fix"), - matches.contains_id("allow-dirty"), - matches.contains_id("allow-staged"), + matches.get_flag("fix"), + matches.get_flag("allow-dirty"), + matches.get_flag("allow-staged"), ); }, Some(("fmt", matches)) => { - fmt::run(matches.contains_id("check"), matches.contains_id("verbose")); + fmt::run(matches.get_flag("check"), matches.get_flag("verbose")); }, Some(("update_lints", matches)) => { - if matches.contains_id("print-only") { + if matches.get_flag("print-only") { update_lints::print_lints(); - } else if matches.contains_id("check") { + } else if matches.get_flag("check") { update_lints::update(update_lints::UpdateMode::Check); } else { update_lints::update(update_lints::UpdateMode::Change); @@ -38,7 +38,7 @@ fn main() { matches.get_one::("name"), matches.get_one::("category").map(String::as_str), matches.get_one::("type").map(String::as_str), - matches.contains_id("msrv"), + matches.get_flag("msrv"), ) { Ok(_) => update_lints::update(update_lints::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {e}"), @@ -46,7 +46,7 @@ fn main() { }, Some(("setup", sub_command)) => match sub_command.subcommand() { Some(("intellij", matches)) => { - if matches.contains_id("remove") { + if matches.get_flag("remove") { setup::intellij::remove_rustc_src(); } else { setup::intellij::setup_rustc_src( @@ -57,17 +57,17 @@ fn main() { } }, Some(("git-hook", matches)) => { - if matches.contains_id("remove") { + if matches.get_flag("remove") { setup::git_hook::remove_hook(); } else { - setup::git_hook::install_hook(matches.contains_id("force-override")); + setup::git_hook::install_hook(matches.get_flag("force-override")); } }, Some(("vscode-tasks", matches)) => { - if matches.contains_id("remove") { + if matches.get_flag("remove") { setup::vscode::remove_tasks(); } else { - setup::vscode::install_tasks(matches.contains_id("force-override")); + setup::vscode::install_tasks(matches.get_flag("force-override")); } }, _ => {}, @@ -91,7 +91,7 @@ fn main() { Some(("rename_lint", matches)) => { let old_name = matches.get_one::("old_name").unwrap(); let new_name = matches.get_one::("new_name").unwrap_or(old_name); - let uplift = matches.contains_id("uplift"); + let uplift = matches.get_flag("uplift"); update_lints::rename(old_name, new_name, uplift); }, Some(("deprecate", matches)) => { @@ -110,24 +110,37 @@ fn get_clap_config() -> ArgMatches { Command::new("bless").about("bless the test output changes").arg( Arg::new("ignore-timestamp") .long("ignore-timestamp") + .action(ArgAction::SetTrue) .help("Include files updated before clippy was built"), ), Command::new("dogfood").about("Runs the dogfood test").args([ - Arg::new("fix").long("fix").help("Apply the suggestions when possible"), + Arg::new("fix") + .long("fix") + .action(ArgAction::SetTrue) + .help("Apply the suggestions when possible"), Arg::new("allow-dirty") .long("allow-dirty") + .action(ArgAction::SetTrue) .help("Fix code even if the working directory has changes") .requires("fix"), Arg::new("allow-staged") .long("allow-staged") + .action(ArgAction::SetTrue) .help("Fix code even if the working directory has staged changes") .requires("fix"), ]), Command::new("fmt") .about("Run rustfmt on all projects and tests") .args([ - Arg::new("check").long("check").help("Use the rustfmt --check option"), - Arg::new("verbose").short('v').long("verbose").help("Echo commands run"), + Arg::new("check") + .long("check") + .action(ArgAction::SetTrue) + .help("Use the rustfmt --check option"), + Arg::new("verbose") + .short('v') + .long("verbose") + .action(ArgAction::SetTrue) + .help("Echo commands run"), ]), Command::new("update_lints") .about("Updates lint registration and information from the source code") @@ -140,13 +153,17 @@ fn get_clap_config() -> ArgMatches { * all lints are registered in the lint store", ) .args([ - Arg::new("print-only").long("print-only").help( - "Print a table of lints to STDOUT. \ - This does not include deprecated and internal lints. \ - (Does not modify any files)", - ), + Arg::new("print-only") + .long("print-only") + .action(ArgAction::SetTrue) + .help( + "Print a table of lints to STDOUT. \ + This does not include deprecated and internal lints. \ + (Does not modify any files)", + ), Arg::new("check") .long("check") + .action(ArgAction::SetTrue) .help("Checks that `cargo dev update_lints` has been run. Used on CI."), ]), Command::new("new_lint") @@ -156,15 +173,13 @@ fn get_clap_config() -> ArgMatches { .short('p') .long("pass") .help("Specify whether the lint runs during the early or late pass") - .takes_value(true) - .value_parser([PossibleValue::new("early"), PossibleValue::new("late")]) + .value_parser(["early", "late"]) .conflicts_with("type") .required_unless_present("type"), Arg::new("name") .short('n') .long("name") .help("Name of the new lint in snake case, ex: fn_too_long") - .takes_value(true) .required(true), Arg::new("category") .short('c') @@ -172,25 +187,23 @@ fn get_clap_config() -> ArgMatches { .help("What category the lint belongs to") .default_value("nursery") .value_parser([ - PossibleValue::new("style"), - PossibleValue::new("correctness"), - PossibleValue::new("suspicious"), - PossibleValue::new("complexity"), - PossibleValue::new("perf"), - PossibleValue::new("pedantic"), - PossibleValue::new("restriction"), - PossibleValue::new("cargo"), - PossibleValue::new("nursery"), - PossibleValue::new("internal"), - PossibleValue::new("internal_warn"), - ]) - .takes_value(true), - Arg::new("type") - .long("type") - .help("What directory the lint belongs in") - .takes_value(true) - .required(false), - Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"), + "style", + "correctness", + "suspicious", + "complexity", + "perf", + "pedantic", + "restriction", + "cargo", + "nursery", + "internal", + "internal_warn", + ]), + Arg::new("type").long("type").help("What directory the lint belongs in"), + Arg::new("msrv") + .long("msrv") + .action(ArgAction::SetTrue) + .help("Add MSRV config code to the lint"), ]), Command::new("setup") .about("Support for setting up your personal development environment") @@ -201,13 +214,12 @@ fn get_clap_config() -> ArgMatches { .args([ Arg::new("remove") .long("remove") - .help("Remove the dependencies added with 'cargo dev setup intellij'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the dependencies added with 'cargo dev setup intellij'"), Arg::new("rustc-repo-path") .long("repo-path") .short('r') .help("The path to a rustc repo that will be used for setting the dependencies") - .takes_value(true) .value_name("path") .conflicts_with("remove") .required(true), @@ -217,26 +229,26 @@ fn get_clap_config() -> ArgMatches { .args([ Arg::new("remove") .long("remove") - .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"), Arg::new("force-override") .long("force-override") .short('f') - .help("Forces the override of an existing git pre-commit hook") - .required(false), + .action(ArgAction::SetTrue) + .help("Forces the override of an existing git pre-commit hook"), ]), Command::new("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") .args([ Arg::new("remove") .long("remove") - .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the tasks added with 'cargo dev setup vscode-tasks'"), Arg::new("force-override") .long("force-override") .short('f') - .help("Forces the override of existing vscode tasks") - .required(false), + .action(ArgAction::SetTrue) + .help("Forces the override of existing vscode tasks"), ]), ]), Command::new("remove") @@ -295,6 +307,7 @@ fn get_clap_config() -> ArgMatches { .help("The new name of the lint"), Arg::new("uplift") .long("uplift") + .action(ArgAction::SetTrue) .help("This lint will be uplifted into rustc"), ]), Command::new("deprecate").about("Deprecates the given lint").args([ @@ -305,8 +318,6 @@ fn get_clap_config() -> ArgMatches { Arg::new("reason") .long("reason") .short('r') - .required(false) - .takes_value(true) .help("The reason for deprecation"), ]), ]) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 4c40483e3ec..796f1ff1695 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["clippy", "lint", "plugin"] edition = "2021" [dependencies] -cargo_metadata = "0.14" +cargo_metadata = "0.15.3" clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } if_chain = "1.0" diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 556fa579000..1d9096ea64d 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -34,14 +35,16 @@ declare_clippy_lint! { declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]); -fn is_bool_lit(e: &Expr<'_>) -> bool { - matches!( - e.kind, - ExprKind::Lit(Lit { - node: LitKind::Bool(_), - .. - }) - ) && !e.span.from_expansion() +fn extract_bool_lit(e: &Expr<'_>) -> Option { + if let ExprKind::Lit(Lit { + node: LitKind::Bool(b), .. + }) = e.kind + && !e.span.from_expansion() + { + Some(b) + } else { + None + } } fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { @@ -69,24 +72,23 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; let macro_name = cx.tcx.item_name(macro_call.def_id); - if !matches!( - macro_name.as_str(), - "assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne" - ) { - return; - } + let eq_macro = match macro_name.as_str() { + "assert_eq" | "debug_assert_eq" => true, + "assert_ne" | "debug_assert_ne" => false, + _ => return, + }; let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; let a_span = a.span.source_callsite(); let b_span = b.span.source_callsite(); - let (lit_span, non_lit_expr) = match (is_bool_lit(a), is_bool_lit(b)) { - // assert_eq!(true, b) - // ^^^^^^ - (true, false) => (a_span.until(b_span), b), - // assert_eq!(a, true) - // ^^^^^^ - (false, true) => (b_span.with_lo(a_span.hi()), a), + let (lit_span, bool_value, non_lit_expr) = match (extract_bool_lit(a), extract_bool_lit(b)) { + // assert_eq!(true/false, b) + // ^^^^^^^^^^^^ + (Some(bool_value), None) => (a_span.until(b_span), bool_value, b), + // assert_eq!(a, true/false) + // ^^^^^^^^^^^^ + (None, Some(bool_value)) => (b_span.with_lo(a_span.hi()), bool_value, a), // If there are two boolean arguments, we definitely don't understand // what's going on, so better leave things as is... // @@ -121,9 +123,16 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { // ^^^^^^^^^ let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); + let mut suggestions = vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())]; + + if bool_value ^ eq_macro { + let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) else { return }; + suggestions.push((non_lit_expr.span, (!sugg).to_string())); + } + diag.multipart_suggestion( format!("replace it with `{non_eq_mac}!(..)`"), - vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())], + suggestions, Applicability::MachineApplicable, ); }, diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 36a366fc974..457a25826e7 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -156,6 +156,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO, crate::exit::EXIT_INFO, crate::explicit_write::EXPLICIT_WRITE_INFO, + crate::extra_unused_type_parameters::EXTRA_UNUSED_TYPE_PARAMETERS_INFO, crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO, crate::float_literal::EXCESSIVE_PRECISION_INFO, crate::float_literal::LOSSY_FLOAT_LITERAL_INFO, diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 9d089fcad70..aef2db38583 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -4,8 +4,8 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; use rustc_span::def_id::LocalDefId; +use rustc_span::Span; use rustc_target::spec::abi::Abi; declare_clippy_lint! { diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs new file mode 100644 index 00000000000..2fdd8a71466 --- /dev/null +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -0,0 +1,178 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::trait_ref_of_method; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::MultiSpan; +use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor}; +use rustc_hir::{ + GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, PredicateOrigin, Ty, TyKind, WherePredicate, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{def_id::DefId, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for type parameters in generics that are never used anywhere else. + /// + /// ### Why is this bad? + /// Functions cannot infer the value of unused type parameters; therefore, calling them + /// requires using a turbofish, which serves no purpose but to satisfy the compiler. + /// + /// ### Example + /// ```rust + /// // unused type parameters + /// fn unused_ty(x: u8) { + /// // .. + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn no_unused_ty(x: u8) { + /// // .. + /// } + /// ``` + #[clippy::version = "1.69.0"] + pub EXTRA_UNUSED_TYPE_PARAMETERS, + complexity, + "unused type parameters in function definitions" +} +declare_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]); + +/// A visitor struct that walks a given function and gathers generic type parameters, plus any +/// trait bounds those parameters have. +struct TypeWalker<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + /// Collection of all the type parameters and their spans. + ty_params: FxHashMap, + /// Collection of any (inline) trait bounds corresponding to each type parameter. + bounds: FxHashMap, + /// The entire `Generics` object of the function, useful for querying purposes. + generics: &'tcx Generics<'tcx>, + /// The value of this will remain `true` if *every* parameter: + /// 1. Is a type parameter, and + /// 2. Goes unused in the function. + /// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic + /// parameters are present, this will be set to `false`. + all_params_unused: bool, +} + +impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { + fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> Self { + let mut all_params_unused = true; + let ty_params = generics + .params + .iter() + .filter_map(|param| { + if let GenericParamKind::Type { .. } = param.kind { + Some((param.def_id.into(), param.span)) + } else { + if !param.is_elided_lifetime() { + all_params_unused = false; + } + None + } + }) + .collect(); + Self { + cx, + ty_params, + bounds: FxHashMap::default(), + generics, + all_params_unused, + } + } + + fn emit_lint(&self) { + let (msg, help) = match self.ty_params.len() { + 0 => return, + 1 => ( + "type parameter goes unused in function definition", + "consider removing the parameter", + ), + _ => ( + "type parameters go unused in function definition", + "consider removing the parameters", + ), + }; + + let source_map = self.cx.tcx.sess.source_map(); + let span = if self.all_params_unused { + self.generics.span.into() // Remove the entire list of generics + } else { + MultiSpan::from_spans( + self.ty_params + .iter() + .map(|(def_id, &span)| { + // Extend the span past any trait bounds, and include the comma at the end. + let span_to_extend = self.bounds.get(def_id).copied().map_or(span, Span::shrink_to_hi); + let comma_range = source_map.span_extend_to_next_char(span_to_extend, '>', false); + let comma_span = source_map.span_through_char(comma_range, ','); + span.with_hi(comma_span.hi()) + }) + .collect(), + ) + }; + + span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, span, msg, None, help); + } +} + +impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { + if let Some((def_id, _)) = t.peel_refs().as_generic_param() { + if self.ty_params.remove(&def_id).is_some() { + self.all_params_unused = false; + } + } else if let TyKind::OpaqueDef(id, _, _) = t.kind { + // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls + // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're + // using `OnlyBodies`, so the check ends up failing and the type isn't fully walked. + let item = self.nested_visit_map().item(id); + walk_item(self, item); + } else { + walk_ty(self, t); + } + } + + fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) { + if let WherePredicate::BoundPredicate(predicate) = predicate { + // Collect spans for bounds that appear in the list of generics (not in a where-clause) + // for use in forming the help message + if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() + && let PredicateOrigin::GenericParam = predicate.origin + { + self.bounds.insert(def_id, predicate.span); + } + // Only walk the right-hand side of where-bounds + for bound in predicate.bounds { + walk_param_bound(self, bound); + } + } + } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } +} + +impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Fn(_, generics, _) = item.kind { + let mut walker = TypeWalker::new(cx, generics); + walk_item(&mut walker, item); + walker.emit_lint(); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { + // Only lint on inherent methods, not trait methods. + if let ImplItemKind::Fn(..) = item.kind && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { + let mut walker = TypeWalker::new(cx, item.generics); + walk_impl_item(&mut walker, item); + walker.emit_lint(); + } + } +} diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index d376ad3bfe3..ea26b96ee07 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -125,7 +125,7 @@ declare_clippy_lint! { /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.66.0"] pub UNINLINED_FORMAT_ARGS, - style, + pedantic, "using non-inlined variables in `format!` calls" } diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs index d6b50537c2e..8b53ee68ebd 100644 --- a/clippy_lints/src/functions/misnamed_getters.rs +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -10,13 +10,7 @@ use std::iter; use super::MISNAMED_GETTERS; -pub fn check_fn( - cx: &LateContext<'_>, - kind: FnKind<'_>, - decl: &FnDecl<'_>, - body: &Body<'_>, - span: Span, -) { +pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: &Body<'_>, span: Span) { let FnKind::Method(ref ident, sig) = kind else { return; }; diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index a13909a2cdb..f2aa7b597a7 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -25,7 +25,7 @@ pub(super) fn check_fn<'tcx>( intravisit::FnKind::Closure => return, }; - check_raw_ptr(cx, unsafety, decl, body, def_id) + check_raw_ptr(cx, unsafety, decl, body, def_id); } pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index 7557a9ce13f..c924d7361ce 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -66,7 +66,9 @@ impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator { fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) { if sig.decl.implicit_self.has_implicit_self() { - let ret_ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(fn_id).subst_identity().output()); + let ret_ty = cx + .tcx + .erase_late_bound_regions(cx.tcx.fn_sig(fn_id).subst_identity().output()); let ret_ty = cx .tcx .try_normalize_erasing_regions(cx.param_env, ret_ty) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 80ed2862a41..e13bc47973b 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -135,6 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if item.ident.name == sym::len; if let ImplItemKind::Fn(sig, _) = &item.kind; if sig.decl.implicit_self.has_implicit_self(); + if sig.decl.inputs.len() == 1; if cx.effective_visibilities.is_exported(item.owner_id.def_id); if matches!(sig.decl.output, FnRetTy::Return(_)); if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()); @@ -196,7 +197,15 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool { item.ident.name == name && if let AssocItemKind::Fn { has_self } = item.kind { - has_self && { cx.tcx.fn_sig(item.id.owner_id).skip_binder().inputs().skip_binder().len() == 1 } + has_self && { + cx.tcx + .fn_sig(item.id.owner_id) + .skip_binder() + .inputs() + .skip_binder() + .len() + == 1 + } } else { false } @@ -342,7 +351,11 @@ fn check_for_is_empty<'tcx>( ), Some(is_empty) if !(is_empty.fn_has_self_parameter - && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(), self_kind, output)) => + && check_is_empty_sig( + cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(), + self_kind, + output, + )) => { ( format!( diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d0683039776..565c5b7af00 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -121,6 +121,7 @@ mod excessive_bools; mod exhaustive_items; mod exit; mod explicit_write; +mod extra_unused_type_parameters; mod fallible_impl_from; mod float_literal; mod floating_point_arithmetic; @@ -909,6 +910,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)); + store.register_late_pass(|_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 747a94ba5a6..43a1a65a43a 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,20 +1,21 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, }; -use rustc_hir::lang_items; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, PolyTraitRef, PredicateOrigin, TraitFn, - TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + lang_items, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, + Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, + PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter as middle_nested_filter; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; @@ -33,8 +34,6 @@ declare_clippy_lint! { /// ### Known problems /// - We bail out if the function has a `where` clause where lifetimes /// are mentioned due to potential false positives. - /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the - /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`. /// /// ### Example /// ```rust @@ -92,7 +91,7 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, generics, id) = item.kind { - check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true); + check_fn_inner(cx, sig, Some(id), None, generics, item.span, true); } else if let ItemKind::Impl(impl_) = item.kind { if !item.span.from_expansion() { report_extra_impl_lifetimes(cx, impl_); @@ -105,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id.def_id).is_none(); check_fn_inner( cx, - sig.decl, + sig, Some(id), None, item.generics, @@ -121,29 +120,21 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true); + check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true); } } } -/// The lifetime of a &-reference. -#[derive(PartialEq, Eq, Hash, Debug, Clone)] -enum RefLt { - Unnamed, - Static, - Named(LocalDefId), -} - fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, - decl: &'tcx FnDecl<'_>, + sig: &'tcx FnSig<'_>, body: Option, trait_sig: Option<&[Ident]>, generics: &'tcx Generics<'_>, span: Span, report_extra_lifetimes: bool, ) { - if span.from_expansion() || has_where_lifetimes(cx, generics) { + if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) { return; } @@ -162,7 +153,7 @@ fn check_fn_inner<'tcx>( for bound in pred.bounds { let mut visitor = RefVisitor::new(cx); walk_param_bound(&mut visitor, bound); - if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { + if visitor.lts.iter().any(|lt| matches!(lt.res, LifetimeName::Param(_))) { return; } if let GenericBound::Trait(ref trait_ref, _) = *bound { @@ -189,12 +180,12 @@ fn check_fn_inner<'tcx>( } } - if let Some(elidable_lts) = could_use_elision(cx, decl, body, trait_sig, generics.params) { + if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) { let lts = elidable_lts .iter() // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a // `Node::GenericParam`. - .filter_map(|&(def_id, _)| cx.tcx.hir().get_by_def_id(def_id).ident()) + .filter_map(|&def_id| cx.tcx.hir().get_by_def_id(def_id).ident()) .map(|ident| ident.to_string()) .collect::>() .join(", "); @@ -202,21 +193,99 @@ fn check_fn_inner<'tcx>( span_lint_and_then( cx, NEEDLESS_LIFETIMES, - span.with_hi(decl.output.span().hi()), + span.with_hi(sig.decl.output.span().hi()), &format!("the following explicit lifetimes could be elided: {lts}"), |diag| { - if let Some(span) = elidable_lts.iter().find_map(|&(_, span)| span) { - diag.span_help(span, "replace with `'_` in generic arguments such as here"); + if sig.header.is_async() { + // async functions have usages whose spans point at the lifetime declaration which messes up + // suggestions + return; + }; + + if let Some(suggestions) = elision_suggestions(cx, generics, &elidable_lts, &usages) { + diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); } }, ); } if report_extra_lifetimes { - self::report_extra_lifetimes(cx, decl, generics); + self::report_extra_lifetimes(cx, sig.decl, generics); } } +fn elision_suggestions( + cx: &LateContext<'_>, + generics: &Generics<'_>, + elidable_lts: &[LocalDefId], + usages: &[Lifetime], +) -> Option> { + let explicit_params = generics + .params + .iter() + .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) + .collect::>(); + + let mut suggestions = if elidable_lts.len() == explicit_params.len() { + // if all the params are elided remove the whole generic block + // + // fn x<'a>() {} + // ^^^^ + vec![(generics.span, String::new())] + } else { + elidable_lts + .iter() + .map(|&id| { + let pos = explicit_params.iter().position(|param| param.def_id == id)?; + let param = explicit_params.get(pos)?; + + let span = if let Some(next) = explicit_params.get(pos + 1) { + // fn x<'prev, 'a, 'next>() {} + // ^^^^ + param.span.until(next.span) + } else { + // `pos` should be at least 1 here, because the param in position 0 would either have a `next` + // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. + let prev = explicit_params.get(pos - 1)?; + + // fn x<'prev, 'a>() {} + // ^^^^ + param.span.with_lo(prev.span.hi()) + }; + + Some((span, String::new())) + }) + .collect::>>()? + }; + + suggestions.extend( + usages + .iter() + .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id))) + .map(|usage| { + match cx.tcx.hir().get_parent(usage.hir_id) { + Node::Ty(Ty { + kind: TyKind::Ref(..), .. + }) => { + // expand `&'a T` to `&'a T` + // ^^ ^^^ + let span = cx + .sess() + .source_map() + .span_extend_while(usage.ident.span, |ch| ch.is_ascii_whitespace()) + .unwrap_or(usage.ident.span); + + (span, String::new()) + }, + // `T<'a>` and `impl Foo + 'a` should be replaced by `'_` + _ => (usage.ident.span, String::from("'_")), + } + }), + ); + + Some(suggestions) +} + // elision doesn't work for explicit self types, see rust-lang/rust#69064 fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option) -> bool { if_chain! { @@ -236,13 +305,20 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: } } +fn named_lifetime(lt: &Lifetime) -> Option { + match lt.res { + LifetimeName::Param(id) if !lt.is_anonymous() => Some(id), + _ => None, + } +} + fn could_use_elision<'tcx>( cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, body: Option, trait_sig: Option<&[Ident]>, named_generics: &'tcx [GenericParam<'_>], -) -> Option)>> { +) -> Option<(Vec, Vec)> { // There are two scenarios where elision works: // * no output references, all input references have different LT // * output references, exactly one input reference with same LT @@ -300,32 +376,24 @@ fn could_use_elision<'tcx>( // check for lifetimes from higher scopes for lt in input_lts.iter().chain(output_lts.iter()) { - if !allowed_lts.contains(lt) { + if let Some(id) = named_lifetime(lt) + && !allowed_lts.contains(&id) + { return None; } } // check for higher-ranked trait bounds if !input_visitor.nested_elision_site_lts.is_empty() || !output_visitor.nested_elision_site_lts.is_empty() { - let allowed_lts: FxHashSet<_> = allowed_lts - .iter() - .filter_map(|lt| match lt { - RefLt::Named(def_id) => Some(cx.tcx.item_name(def_id.to_def_id())), - _ => None, - }) - .collect(); + let allowed_lts: FxHashSet<_> = allowed_lts.iter().map(|id| cx.tcx.item_name(id.to_def_id())).collect(); for lt in input_visitor.nested_elision_site_lts { - if let RefLt::Named(def_id) = lt { - if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { - return None; - } + if allowed_lts.contains(<.ident.name) { + return None; } } for lt in output_visitor.nested_elision_site_lts { - if let RefLt::Named(def_id) = lt { - if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { - return None; - } + if allowed_lts.contains(<.ident.name) { + return None; } } } @@ -337,15 +405,10 @@ fn could_use_elision<'tcx>( let elidable_lts = named_lifetime_occurrences(&input_lts) .into_iter() .filter_map(|(def_id, occurrences)| { - if occurrences == 1 && (input_lts.len() == 1 || !output_lts.contains(&RefLt::Named(def_id))) { - Some(( - def_id, - input_visitor - .lifetime_generic_arg_spans - .get(&def_id) - .or_else(|| output_visitor.lifetime_generic_arg_spans.get(&def_id)) - .copied(), - )) + if occurrences == 1 + && (input_lts.len() == 1 || !output_lts.iter().any(|lt| named_lifetime(lt) == Some(def_id))) + { + Some(def_id) } else { None } @@ -353,31 +416,34 @@ fn could_use_elision<'tcx>( .collect::>(); if elidable_lts.is_empty() { - None - } else { - Some(elidable_lts) + return None; } + + let usages = itertools::chain(input_lts, output_lts).collect(); + + Some((elidable_lts, usages)) } -fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { - let mut allowed_lts = FxHashSet::default(); - for par in named_generics.iter() { - if let GenericParamKind::Lifetime { .. } = par.kind { - allowed_lts.insert(RefLt::Named(par.def_id)); - } - } - allowed_lts.insert(RefLt::Unnamed); - allowed_lts.insert(RefLt::Static); - allowed_lts +fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { + named_generics + .iter() + .filter_map(|par| { + if let GenericParamKind::Lifetime { .. } = par.kind { + Some(par.def_id) + } else { + None + } + }) + .collect() } /// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve /// relative order. #[must_use] -fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> { +fn named_lifetime_occurrences(lts: &[Lifetime]) -> Vec<(LocalDefId, usize)> { let mut occurrences = Vec::new(); for lt in lts { - if let &RefLt::Named(curr_def_id) = lt { + if let Some(curr_def_id) = named_lifetime(lt) { if let Some(pair) = occurrences .iter_mut() .find(|(prev_def_id, _)| *prev_def_id == curr_def_id) @@ -391,12 +457,10 @@ fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> { occurrences } -/// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - lts: Vec, - lifetime_generic_arg_spans: FxHashMap, - nested_elision_site_lts: Vec, + lts: Vec, + nested_elision_site_lts: Vec, unelided_trait_object_lifetime: bool, } @@ -405,32 +469,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { Self { cx, lts: Vec::new(), - lifetime_generic_arg_spans: FxHashMap::default(), nested_elision_site_lts: Vec::new(), unelided_trait_object_lifetime: false, } } - fn record(&mut self, lifetime: &Option) { - if let Some(ref lt) = *lifetime { - if lt.is_static() { - self.lts.push(RefLt::Static); - } else if lt.is_anonymous() { - // Fresh lifetimes generated should be ignored. - self.lts.push(RefLt::Unnamed); - } else if let LifetimeName::Param(def_id) = lt.res { - self.lts.push(RefLt::Named(def_id)); - } - } else { - self.lts.push(RefLt::Unnamed); - } - } - - fn all_lts(&self) -> Vec { + fn all_lts(&self) -> Vec { self.lts .iter() .chain(self.nested_elision_site_lts.iter()) - .cloned() + .copied() .collect::>() } @@ -442,7 +490,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - self.record(&Some(*lifetime)); + self.lts.push(*lifetime); } fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>) { @@ -467,11 +515,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { walk_item(self, item); self.lts.truncate(len); self.lts.extend(bounds.iter().filter_map(|bound| match bound { - GenericArg::Lifetime(l) => Some(if let LifetimeName::Param(def_id) = l.res { - RefLt::Named(def_id) - } else { - RefLt::Unnamed - }), + GenericArg::Lifetime(&l) => Some(l), _ => None, })); }, @@ -491,13 +535,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { _ => walk_ty(self, ty), } } - - fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) { - if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res { - self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span); - } - walk_generic_arg(self, generic_arg); - } } /// Are any lifetimes mentioned in the `where` clause? If so, we don't try to @@ -521,8 +558,12 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) { - return true; + for lt in visitor.all_lts() { + if let Some(id) = named_lifetime(<) + && !allowed_lts.contains(&id) + { + return true; + } } }, WherePredicate::EqPredicate(ref pred) => { diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 8e52cac4323..610a0233eee 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -61,7 +61,8 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Just iterating the collection itself makes the intent - /// more clear and is probably faster. + /// more clear and is probably faster because it eliminates + /// the bounds check that is done when indexing. /// /// ### Example /// ```rust diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index e6ed4ea7a5d..25a1a5842f7 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -149,7 +149,7 @@ pub(super) fn check<'tcx>( |diag| { multispan_sugg( diag, - "consider using an iterator", + "consider using an iterator and enumerate()", vec![ (pat.span, format!("({}, )", ident.name)), ( diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 4277455a3a2..ce5d657bcf0 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,7 +1,6 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::macros::root_macro_call; use clippy_utils::{is_else_clause, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; @@ -38,57 +37,57 @@ declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); impl<'tcx> LateLintPass<'tcx> for ManualAssert { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if_chain! { - if let ExprKind::If(cond, then, None) = expr.kind; - if !matches!(cond.kind, ExprKind::Let(_)); - if !expr.span.from_expansion(); - let then = peel_blocks_with_stmt(then); - if let Some(macro_call) = root_macro_call(then.span); - if cx.tcx.item_name(macro_call.def_id) == sym::panic; - if !cx.tcx.sess.source_map().is_multiline(cond.span); - if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn); + if let ExprKind::If(cond, then, None) = expr.kind + && !matches!(cond.kind, ExprKind::Let(_)) + && !expr.span.from_expansion() + && let then = peel_blocks_with_stmt(then) + && let Some(macro_call) = root_macro_call(then.span) + && cx.tcx.item_name(macro_call.def_id) == sym::panic + && !cx.tcx.sess.source_map().is_multiline(cond.span) + && let Ok(panic_snippet) = cx.sess().source_map().span_to_snippet(macro_call.span) + && let Some(panic_snippet) = panic_snippet.strip_suffix(')') + && let Some((_, format_args_snip)) = panic_snippet.split_once('(') // Don't change `else if foo { panic!(..) }` to `else { assert!(foo, ..) }` as it just // shuffles the condition around. // Should this have a config value? - if !is_else_clause(cx.tcx, expr); - then { - let mut applicability = Applicability::MachineApplicable; - let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); - let cond = cond.peel_drop_temps(); - let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); - if !comments.is_empty() { - comments += "\n"; - } - let (cond, not) = match cond.kind { - ExprKind::Unary(UnOp::Not, e) => (e, ""), - _ => (cond, "!"), - }; - let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); - let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); - // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block - span_lint_and_then( - cx, - MANUAL_ASSERT, - expr.span, - "only a `panic!` in `if`-then statement", - |diag| { - // comments can be noisy, do not show them to the user - if !comments.is_empty() { - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability); - } - diag.span_suggestion( - expr.span, - "try instead", - sugg, - applicability); - } - - ); + && !is_else_clause(cx.tcx, expr) + { + let mut applicability = Applicability::MachineApplicable; + let cond = cond.peel_drop_temps(); + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); + if !comments.is_empty() { + comments += "\n"; } + let (cond, not) = match cond.kind { + ExprKind::Unary(UnOp::Not, e) => (e, ""), + _ => (cond, "!"), + }; + let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); + let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); + // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block + span_lint_and_then( + cx, + MANUAL_ASSERT, + expr.span, + "only a `panic!` in `if`-then statement", + |diag| { + // comments can be noisy, do not show them to the user + if !comments.is_empty() { + diag.tool_only_span_suggestion( + expr.span.shrink_to_lo(), + "add comments back", + comments, + applicability + ); + } + diag.span_suggestion( + expr.span, + "try instead", + sugg, + applicability + ); + } + ); } } } diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 59de8c0384b..3126b590180 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -45,8 +45,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // Accumulate the variants which should be put in place of the wildcard because they're not // already covered. - let has_hidden = adt_def.variants().iter().any(|x| is_hidden(cx, x)); - let mut missing_variants: Vec<_> = adt_def.variants().iter().filter(|x| !is_hidden(cx, x)).collect(); + let is_external = adt_def.did().as_local().is_none(); + let has_external_hidden = is_external && adt_def.variants().iter().any(|x| is_hidden(cx, x)); + let mut missing_variants: Vec<_> = adt_def + .variants() + .iter() + .filter(|x| !(is_external && is_hidden(cx, x))) + .collect(); let mut path_prefix = CommonPrefixSearcher::None; for arm in arms { @@ -133,7 +138,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { match missing_variants.as_slice() { [] => (), - [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg( + [x] if !adt_def.is_variant_list_non_exhaustive() && !has_external_hidden => span_lint_and_sugg( cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, @@ -144,7 +149,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { ), variants => { let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); - let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden { + let message = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden { suggestions.push("_".into()); "wildcard matches known variants and will also match future added variants" } else { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fb94dfa5980..f1e8be7f2b8 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1818,6 +1818,7 @@ declare_clippy_lint! { /// - `or_else` to `or` /// - `get_or_insert_with` to `get_or_insert` /// - `ok_or_else` to `ok_or` + /// - `then` to `then_some` (for msrv >= 1.62.0) /// /// ### Why is this bad? /// Using eager evaluation is shorter and simpler in some cases. diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index fe88fa41fd9..e818f1892e5 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_diag_trait_item; use clippy_utils::source::snippet_with_context; use if_chain::if_chain; @@ -17,19 +17,31 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - let input_type = cx.typeck_results().expr_ty(expr); if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind(); if cx.tcx.is_diagnostic_item(sym::Cow, adt.did()); + then { let mut app = Applicability::MaybeIncorrect; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; - span_lint_and_sugg( + span_lint_and_then( cx, SUSPICIOUS_TO_OWNED, expr.span, &with_forced_trimmed_paths!(format!( "this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned" )), - "consider using, depending on intent", - format!("{recv_snip}.clone()` or `{recv_snip}.into_owned()"), - app, + |diag| { + diag.span_suggestion( + expr.span, + "depending on intent, either make the Cow an Owned variant", + format!("{recv_snip}.into_owned()"), + app + ); + diag.span_suggestion( + expr.span, + "or clone the Cow itself", + format!("{recv_snip}.clone()"), + app + ); + } ); return true; } diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 2814c92e67a..63c575fca30 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -10,6 +10,7 @@ use hir::{ use rustc_ast::Mutability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -66,7 +67,7 @@ declare_lint_pass!(MultipleUnsafeOpsPerBlock => [MULTIPLE_UNSAFE_OPS_PER_BLOCK]) impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) { + if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) || in_external_macro(cx.tcx.sess, block.span) { return; } let mut unsafe_ops = vec![]; diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 472f52380bb..c5ea09590d3 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -25,11 +25,11 @@ declare_clippy_lint! { /// Using the dedicated functions of the `Option` type is clearer and /// more concise than an `if let` expression. /// - /// ### Known problems - /// This lint uses a deliberately conservative metric for checking - /// if the inside of either body contains breaks or continues which will - /// cause it to not suggest a fix if either block contains a loop with - /// continues or breaks contained within the loop. + /// ### Notes + /// This lint uses a deliberately conservative metric for checking if the + /// inside of either body contains loop control expressions `break` or + /// `continue` (which cannot be used within closures). If these are found, + /// this lint will not be raised. /// /// ### Example /// ```rust diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 8afe286fbd5..d88409c356e 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -624,7 +624,10 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: return; }; - match *self.cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i].peel_refs().kind() { + match *self.cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i] + .peel_refs() + .kind() + { ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => { set_skip_flag(); }, diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 1fda58fa54d..9e6c6c73d4f 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -1,5 +1,8 @@ +use std::fmt::Display; + use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::source::snippet_opt; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{LitKind, StrStyle}; @@ -77,13 +80,45 @@ impl<'tcx> LateLintPass<'tcx> for Regex { } } -#[must_use] -fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u8) -> Span { - let offset = u32::from(offset); - let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset); - let start = base.lo() + BytePos(u32::try_from(c.start.offset).expect("offset too large") + offset); - assert!(start <= end); - Span::new(start, end, base.ctxt(), base.parent()) +fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescaped: &str, base: Span, offset: u8) { + let parts: Option<(_, _, &dyn Display)> = match &error { + regex_syntax::Error::Parse(e) => Some((e.span(), e.auxiliary_span(), e.kind())), + regex_syntax::Error::Translate(e) => Some((e.span(), None, e.kind())), + _ => None, + }; + + let convert_span = |regex_span: ®ex_syntax::ast::Span| { + let offset = u32::from(offset); + let start = base.lo() + BytePos(u32::try_from(regex_span.start.offset).expect("offset too large") + offset); + let end = base.lo() + BytePos(u32::try_from(regex_span.end.offset).expect("offset too large") + offset); + + Span::new(start, end, base.ctxt(), base.parent()) + }; + + if let Some((primary, auxiliary, kind)) = parts + && let Some(literal_snippet) = snippet_opt(cx, base) + && let Some(inner) = literal_snippet.get(offset as usize..) + // Only convert to native rustc spans if the parsed regex matches the + // source snippet exactly, to ensure the span offsets are correct + && inner.get(..unescaped.len()) == Some(unescaped) + { + let spans = if let Some(auxiliary) = auxiliary { + vec![convert_span(primary), convert_span(auxiliary)] + } else { + vec![convert_span(primary)] + }; + + span_lint(cx, INVALID_REGEX, spans, &format!("regex syntax error: {kind}")); + } else { + span_lint_and_help( + cx, + INVALID_REGEX, + base, + &error.to_string(), + None, + "consider using a raw string literal: `r\"..\"`", + ); + } } fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option { @@ -155,25 +190,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); } }, - Err(regex_syntax::Error::Parse(e)) => { - span_lint( - cx, - INVALID_REGEX, - str_span(expr.span, *e.span(), offset), - &format!("regex syntax error: {}", e.kind()), - ); - }, - Err(regex_syntax::Error::Translate(e)) => { - span_lint( - cx, - INVALID_REGEX, - str_span(expr.span, *e.span(), offset), - &format!("regex syntax error: {}", e.kind()), - ); - }, - Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); - }, + Err(e) => lint_syntax_error(cx, &e, r, expr.span, offset), } } } else if let Some(r) = const_str(cx, expr) { @@ -183,25 +200,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); } }, - Err(regex_syntax::Error::Parse(e)) => { - span_lint( - cx, - INVALID_REGEX, - expr.span, - &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()), - ); - }, - Err(regex_syntax::Error::Translate(e)) => { - span_lint( - cx, - INVALID_REGEX, - expr.span, - &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()), - ); - }, - Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); - }, + Err(e) => span_lint(cx, INVALID_REGEX, expr.span, &e.to_string()), } } } diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 8c39b4fc569..bccf421e8f3 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -78,8 +78,8 @@ fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, spa // We don't want to emit this lint if the `#[must_use]` attribute is already there. if !cx.tcx.hir().attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use)); if cx.tcx.visibility(fn_def.to_def_id()).is_public(); - let ret_ty = return_ty(cx, owner_id.into()); - let self_arg = nth_arg(cx, owner_id.into(), 0); + let ret_ty = return_ty(cx, owner_id); + let self_arg = nth_arg(cx, owner_id, 0); // If `Self` has the same type as the returned type, then we want to warn. // // For this check, we don't want to remove the reference on the returned type because if diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 8f1d1490e1f..34a3e5ddf4f 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// # let x = 0; /// unsafe { f(x); } /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.68.0"] pub SEMICOLON_INSIDE_BLOCK, restriction, "add a semicolon inside the block" @@ -59,7 +59,7 @@ declare_clippy_lint! { /// # let x = 0; /// unsafe { f(x) }; /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.68.0"] pub SEMICOLON_OUTSIDE_BLOCK, restriction, "add a semicolon outside the block" diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 585e2075fa9..c1f228d5f90 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -392,9 +392,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - let is_exported = cx - .effective_visibilities - .is_exported(field.def_id); + let is_exported = cx.effective_visibilities.is_exported(field.def_id); self.check_ty( cx, diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 92053cec59f..0e526c216be 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } }, hir::ExprKind::MethodCall(path, arg_0, ..) => match path.ident.as_str() { - "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { + "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" | "is_ok" | "is_err" => { check_map_error(cx, arg_0, expr); }, _ => (), diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index f48be27592b..1d78c7cfae0 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -253,7 +253,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN. /// /// The minimum rust version that the project supports (msrv: Option = None), @@ -323,7 +323,7 @@ define_Conf! { /// /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. (trivial_copy_size_limit: Option = None), - /// Lint: LARGE_TYPE_PASS_BY_MOVE. + /// Lint: LARGE_TYPES_PASSED_BY_VALUE. /// /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. (pass_by_value_size_limit: u64 = 256), @@ -411,7 +411,7 @@ define_Conf! { /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed. /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. (max_suggested_slice_pattern_length: u64 = 3), - /// Lint: AWAIT_HOLDING_INVALID_TYPE + /// Lint: AWAIT_HOLDING_INVALID_TYPE. (await_holding_invalid_types: Vec = Vec::new()), /// Lint: LARGE_INCLUDE_FILE. /// @@ -437,7 +437,7 @@ define_Conf! { /// /// The maximum size of the `Err`-variant in a `Result` returned from a function (large_error_threshold: u64 = 128), - /// Lint: MUTABLE_KEY. + /// Lint: MUTABLE_KEY_TYPE. /// /// A list of paths to types that should be treated like `Arc`, i.e. ignored but /// for the generic parameters for determining interior mutability @@ -446,7 +446,7 @@ define_Conf! { /// /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` (allow_mixed_uninlined_format_args: bool = true), - /// Lint: INDEXING_SLICING + /// Lint: INDEXING_SLICING. /// /// Whether to suppress a restriction lint in constant code. In same /// cases the restructured operation might not be unavoidable, as the diff --git a/clippy_lints/src/utils/dump_hir.rs b/clippy_lints/src/utils/dump_hir.rs index 01efc527a8c..092041aecf2 100644 --- a/clippy_lints/src/utils/dump_hir.rs +++ b/clippy_lints/src/utils/dump_hir.rs @@ -1,4 +1,5 @@ use clippy_utils::get_attr; +use hir::TraitItem; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -47,6 +48,18 @@ impl<'tcx> LateLintPass<'tcx> for DumpHir { println!("{stmt:#?}"); } } + + fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { + if has_attr(cx, item.hir_id()) { + println!("{item:#?}"); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &hir::ImplItem<'_>) { + if has_attr(cx, item.hir_id()) { + println!("{item:#?}"); + } + } } fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { 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 4c3b1b131fd..f718207654f 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 @@ -215,14 +215,13 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { cx, }; let body_id = cx.tcx.hir().body_owned_by( - cx.tcx.hir().local_def_id( - impl_item_refs - .iter() - .find(|iiref| iiref.ident.as_str() == "get_lints") - .expect("LintPass needs to implement get_lints") - .id - .hir_id(), - ), + impl_item_refs + .iter() + .find(|iiref| iiref.ident.as_str() == "get_lints") + .expect("LintPass needs to implement get_lints") + .id + .owner_id + .def_id, ); collector.visit_expr(cx.tcx.hir().body(body_id).value); } diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index d7f466c1976..63dccbf697c 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -770,10 +770,7 @@ impl<'tcx> FormatSpec<'tcx> { /// Has no other formatting specifiers than setting the format trait. returns true for `{}`, /// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}` pub fn is_default_for_trait(&self) -> bool { - self.width.is_implied() - && self.precision.is_implied() - && self.align == Alignment::AlignUnknown - && self.no_flags + self.width.is_implied() && self.precision.is_implied() && self.align == Alignment::AlignUnknown && self.no_flags } } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index b8c87aa5e1e..78fb2e0eb7e 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -809,7 +809,10 @@ pub struct DerefClosure { /// /// note: this only works on single line immutable closures with exactly one input parameter. pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Option { - if let hir::ExprKind::Closure(&Closure { fn_decl, def_id, body, .. }) = closure.kind { + if let hir::ExprKind::Closure(&Closure { + fn_decl, def_id, body, .. + }) = closure.kind + { let closure_body = cx.tcx.hir().body(body); // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`) // a type annotation is present if param `kind` is different from `TyKind::Infer` diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index d18b62d1bf1..00073bcd82a 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -392,12 +392,16 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { .cx .typeck_results() .type_dependent_def_id(e.hir_id) - .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe) => + .map_or(false, |id| { + self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe + }) => { self.is_unsafe = true; }, 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().unsafety() == Unsafety::Unsafe => self.is_unsafe = true, + ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe => { + self.is_unsafe = true; + }, ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true, _ => walk_expr(self, e), }, diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index de31c16b819..653121af54d 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -10,8 +10,8 @@ edition = "2021" publish = false [dependencies] -cargo_metadata = "0.14" -clap = "3.2" +cargo_metadata = "0.15.3" +clap = "4.1.4" crossbeam-channel = "0.5.6" flate2 = "1.0" rayon = "1.5.1" diff --git a/rust-toolchain b/rust-toolchain index 4e7fc565a32..adea8c53df2 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-01-27" +channel = "nightly-2023-02-10" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index 2a240cc249b..c1a10ba55ef 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -1,12 +1,3 @@ -error: hardcoded path to a diagnostic item - --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 - | -LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `sym::Deref` - = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` - error: hardcoded path to a diagnostic item --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 | @@ -14,6 +5,15 @@ LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: convert all references to use `sym::deref_method` + = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 + | +LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `sym::Deref` error: hardcoded path to a language item --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index 1e5f20e8c39..a13af565203 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -149,3 +149,22 @@ macro_rules! almost_complete_range { let _ = '0'..'9'; }; } + +#[macro_export] +macro_rules! unsafe_macro { + () => { + unsafe { + *core::ptr::null::<()>(); + *core::ptr::null::<()>(); + } + }; +} + +#[macro_export] +macro_rules! needless_lifetime { + () => { + fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + }; +} diff --git a/tests/ui/bool_assert_comparison.fixed b/tests/ui/bool_assert_comparison.fixed index 95f35a61bb2..b8dd92906c8 100644 --- a/tests/ui/bool_assert_comparison.fixed +++ b/tests/ui/bool_assert_comparison.fixed @@ -86,7 +86,7 @@ fn main() { let b = ImplNotTraitWithBool; assert_eq!("a".len(), 1); - assert!("a".is_empty()); + assert!(!"a".is_empty()); assert!("".is_empty()); assert!("".is_empty()); assert_eq!(a!(), b!()); @@ -97,16 +97,16 @@ fn main() { assert_ne!("a".len(), 1); assert!("a".is_empty()); - assert!("".is_empty()); - assert!("".is_empty()); + assert!(!"".is_empty()); + assert!(!"".is_empty()); assert_ne!(a!(), b!()); assert_ne!(a!(), "".is_empty()); assert_ne!("".is_empty(), b!()); assert_ne!(a, true); - assert!(b); + assert!(!b); debug_assert_eq!("a".len(), 1); - debug_assert!("a".is_empty()); + debug_assert!(!"a".is_empty()); debug_assert!("".is_empty()); debug_assert!("".is_empty()); debug_assert_eq!(a!(), b!()); @@ -117,27 +117,27 @@ fn main() { debug_assert_ne!("a".len(), 1); debug_assert!("a".is_empty()); - debug_assert!("".is_empty()); - debug_assert!("".is_empty()); + debug_assert!(!"".is_empty()); + debug_assert!(!"".is_empty()); debug_assert_ne!(a!(), b!()); debug_assert_ne!(a!(), "".is_empty()); debug_assert_ne!("".is_empty(), b!()); debug_assert_ne!(a, true); - debug_assert!(b); + debug_assert!(!b); // assert with error messages assert_eq!("a".len(), 1, "tadam {}", 1); assert_eq!("a".len(), 1, "tadam {}", true); - assert!("a".is_empty(), "tadam {}", 1); - assert!("a".is_empty(), "tadam {}", true); - assert!("a".is_empty(), "tadam {}", true); + assert!(!"a".is_empty(), "tadam {}", 1); + assert!(!"a".is_empty(), "tadam {}", true); + assert!(!"a".is_empty(), "tadam {}", true); assert_eq!(a, true, "tadam {}", false); debug_assert_eq!("a".len(), 1, "tadam {}", 1); debug_assert_eq!("a".len(), 1, "tadam {}", true); - debug_assert!("a".is_empty(), "tadam {}", 1); - debug_assert!("a".is_empty(), "tadam {}", true); - debug_assert!("a".is_empty(), "tadam {}", true); + debug_assert!(!"a".is_empty(), "tadam {}", 1); + debug_assert!(!"a".is_empty(), "tadam {}", true); + debug_assert!(!"a".is_empty(), "tadam {}", true); debug_assert_eq!(a, true, "tadam {}", false); assert!(a!()); @@ -158,4 +158,14 @@ fn main() { }}; } in_macro!(a); + + assert!("".is_empty()); + assert!("".is_empty()); + assert!(!"requires negation".is_empty()); + assert!(!"requires negation".is_empty()); + + debug_assert!("".is_empty()); + debug_assert!("".is_empty()); + debug_assert!(!"requires negation".is_empty()); + debug_assert!(!"requires negation".is_empty()); } diff --git a/tests/ui/bool_assert_comparison.rs b/tests/ui/bool_assert_comparison.rs index 88e7560b4f9..0a8ad34fda5 100644 --- a/tests/ui/bool_assert_comparison.rs +++ b/tests/ui/bool_assert_comparison.rs @@ -158,4 +158,14 @@ fn main() { }}; } in_macro!(a); + + assert_eq!("".is_empty(), true); + assert_ne!("".is_empty(), false); + assert_ne!("requires negation".is_empty(), true); + assert_eq!("requires negation".is_empty(), false); + + debug_assert_eq!("".is_empty(), true); + debug_assert_ne!("".is_empty(), false); + debug_assert_ne!("requires negation".is_empty(), true); + debug_assert_eq!("requires negation".is_empty(), false); } diff --git a/tests/ui/bool_assert_comparison.stderr b/tests/ui/bool_assert_comparison.stderr index 3d9f8573e61..89cefc95a9f 100644 --- a/tests/ui/bool_assert_comparison.stderr +++ b/tests/ui/bool_assert_comparison.stderr @@ -8,7 +8,7 @@ LL | assert_eq!("a".is_empty(), false); help: replace it with `assert!(..)` | LL - assert_eq!("a".is_empty(), false); -LL + assert!("a".is_empty()); +LL + assert!(!"a".is_empty()); | error: used `assert_eq!` with a literal bool @@ -68,7 +68,7 @@ LL | assert_ne!("".is_empty(), true); help: replace it with `assert!(..)` | LL - assert_ne!("".is_empty(), true); -LL + assert!("".is_empty()); +LL + assert!(!"".is_empty()); | error: used `assert_ne!` with a literal bool @@ -80,7 +80,7 @@ LL | assert_ne!(true, "".is_empty()); help: replace it with `assert!(..)` | LL - assert_ne!(true, "".is_empty()); -LL + assert!("".is_empty()); +LL + assert!(!"".is_empty()); | error: used `assert_ne!` with a literal bool @@ -92,7 +92,7 @@ LL | assert_ne!(b, true); help: replace it with `assert!(..)` | LL - assert_ne!(b, true); -LL + assert!(b); +LL + assert!(!b); | error: used `debug_assert_eq!` with a literal bool @@ -104,7 +104,7 @@ LL | debug_assert_eq!("a".is_empty(), false); help: replace it with `debug_assert!(..)` | LL - debug_assert_eq!("a".is_empty(), false); -LL + debug_assert!("a".is_empty()); +LL + debug_assert!(!"a".is_empty()); | error: used `debug_assert_eq!` with a literal bool @@ -164,7 +164,7 @@ LL | debug_assert_ne!("".is_empty(), true); help: replace it with `debug_assert!(..)` | LL - debug_assert_ne!("".is_empty(), true); -LL + debug_assert!("".is_empty()); +LL + debug_assert!(!"".is_empty()); | error: used `debug_assert_ne!` with a literal bool @@ -176,7 +176,7 @@ LL | debug_assert_ne!(true, "".is_empty()); help: replace it with `debug_assert!(..)` | LL - debug_assert_ne!(true, "".is_empty()); -LL + debug_assert!("".is_empty()); +LL + debug_assert!(!"".is_empty()); | error: used `debug_assert_ne!` with a literal bool @@ -188,7 +188,7 @@ LL | debug_assert_ne!(b, true); help: replace it with `debug_assert!(..)` | LL - debug_assert_ne!(b, true); -LL + debug_assert!(b); +LL + debug_assert!(!b); | error: used `assert_eq!` with a literal bool @@ -200,7 +200,7 @@ LL | assert_eq!("a".is_empty(), false, "tadam {}", 1); help: replace it with `assert!(..)` | LL - assert_eq!("a".is_empty(), false, "tadam {}", 1); -LL + assert!("a".is_empty(), "tadam {}", 1); +LL + assert!(!"a".is_empty(), "tadam {}", 1); | error: used `assert_eq!` with a literal bool @@ -212,7 +212,7 @@ LL | assert_eq!("a".is_empty(), false, "tadam {}", true); help: replace it with `assert!(..)` | LL - assert_eq!("a".is_empty(), false, "tadam {}", true); -LL + assert!("a".is_empty(), "tadam {}", true); +LL + assert!(!"a".is_empty(), "tadam {}", true); | error: used `assert_eq!` with a literal bool @@ -224,7 +224,7 @@ LL | assert_eq!(false, "a".is_empty(), "tadam {}", true); help: replace it with `assert!(..)` | LL - assert_eq!(false, "a".is_empty(), "tadam {}", true); -LL + assert!("a".is_empty(), "tadam {}", true); +LL + assert!(!"a".is_empty(), "tadam {}", true); | error: used `debug_assert_eq!` with a literal bool @@ -236,7 +236,7 @@ LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); help: replace it with `debug_assert!(..)` | LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); -LL + debug_assert!("a".is_empty(), "tadam {}", 1); +LL + debug_assert!(!"a".is_empty(), "tadam {}", 1); | error: used `debug_assert_eq!` with a literal bool @@ -248,7 +248,7 @@ LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true); help: replace it with `debug_assert!(..)` | LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", true); -LL + debug_assert!("a".is_empty(), "tadam {}", true); +LL + debug_assert!(!"a".is_empty(), "tadam {}", true); | error: used `debug_assert_eq!` with a literal bool @@ -260,7 +260,7 @@ LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); help: replace it with `debug_assert!(..)` | LL - debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); -LL + debug_assert!("a".is_empty(), "tadam {}", true); +LL + debug_assert!(!"a".is_empty(), "tadam {}", true); | error: used `assert_eq!` with a literal bool @@ -299,5 +299,101 @@ LL - renamed!(b, true); LL + debug_assert!(b); | -error: aborting due to 25 previous errors +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:162:5 + | +LL | assert_eq!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!("".is_empty(), true); +LL + assert!("".is_empty()); + | + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:163:5 + | +LL | assert_ne!("".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!("".is_empty(), false); +LL + assert!("".is_empty()); + | + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:164:5 + | +LL | assert_ne!("requires negation".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!("requires negation".is_empty(), true); +LL + assert!(!"requires negation".is_empty()); + | + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:165:5 + | +LL | assert_eq!("requires negation".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!("requires negation".is_empty(), false); +LL + assert!(!"requires negation".is_empty()); + | + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:167:5 + | +LL | debug_assert_eq!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("".is_empty(), true); +LL + debug_assert!("".is_empty()); + | + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:168:5 + | +LL | debug_assert_ne!("".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!("".is_empty(), false); +LL + debug_assert!("".is_empty()); + | + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:169:5 + | +LL | debug_assert_ne!("requires negation".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!("requires negation".is_empty(), true); +LL + debug_assert!(!"requires negation".is_empty()); + | + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:170:5 + | +LL | debug_assert_eq!("requires negation".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("requires negation".is_empty(), false); +LL + debug_assert!(!"requires negation".is_empty()); + | + +error: aborting due to 33 previous errors diff --git a/tests/ui/crashes/ice-2774.stderr b/tests/ui/crashes/ice-2774.stderr index 1f26c7f4db6..c5ea0b16d1b 100644 --- a/tests/ui/crashes/ice-2774.stderr +++ b/tests/ui/crashes/ice-2774.stderr @@ -5,6 +5,11 @@ LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` +help: elide the lifetimes + | +LL - pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { +LL + pub fn add_barfoos_to_foos(bars: &HashSet<&Bar>) { + | error: aborting due to previous error diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr index 875d5ab4f21..0b0e0ad2684 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -9,6 +9,11 @@ note: the lint level is defined here | LL | #![deny(clippy::needless_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: elide the lifetimes + | +LL - fn baz<'a>(&'a self) -> impl Foo + 'a { +LL + fn baz(&self) -> impl Foo + '_ { + | error: aborting due to previous error diff --git a/tests/ui/extra_unused_type_parameters.rs b/tests/ui/extra_unused_type_parameters.rs new file mode 100644 index 00000000000..5cb80cb6233 --- /dev/null +++ b/tests/ui/extra_unused_type_parameters.rs @@ -0,0 +1,69 @@ +#![allow(unused, clippy::needless_lifetimes)] +#![warn(clippy::extra_unused_type_parameters)] + +fn unused_ty(x: u8) {} + +fn unused_multi(x: u8) {} + +fn unused_with_lt<'a, T>(x: &'a u8) {} + +fn used_ty(x: T, y: u8) {} + +fn used_ref<'a, T>(x: &'a T) {} + +fn used_ret(x: u8) -> T { + T::default() +} + +fn unused_bounded(x: U) {} + +fn unused_where_clause(x: U) +where + T: Default, +{ +} + +fn some_unused, E>(b: B, c: C) {} + +fn used_opaque(iter: impl Iterator) -> usize { + iter.count() +} + +fn used_ret_opaque() -> impl Iterator { + std::iter::empty() +} + +fn used_vec_box(x: Vec>) {} + +fn used_body() -> String { + T::default().to_string() +} + +fn used_closure() -> impl Fn() { + || println!("{}", T::default().to_string()) +} + +struct S; + +impl S { + fn unused_ty_impl(&self) {} +} + +// Don't lint on trait methods +trait Foo { + fn bar(&self); +} + +impl Foo for S { + fn bar(&self) {} +} + +fn skip_index(iter: Iter, index: usize) -> impl Iterator +where + Iter: Iterator, +{ + iter.enumerate() + .filter_map(move |(i, a)| if i == index { None } else { Some(a) }) +} + +fn main() {} diff --git a/tests/ui/extra_unused_type_parameters.stderr b/tests/ui/extra_unused_type_parameters.stderr new file mode 100644 index 00000000000..1c8dd53e638 --- /dev/null +++ b/tests/ui/extra_unused_type_parameters.stderr @@ -0,0 +1,59 @@ +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:4:13 + | +LL | fn unused_ty(x: u8) {} + | ^^^ + | + = help: consider removing the parameter + = note: `-D clippy::extra-unused-type-parameters` implied by `-D warnings` + +error: type parameters go unused in function definition + --> $DIR/extra_unused_type_parameters.rs:6:16 + | +LL | fn unused_multi(x: u8) {} + | ^^^^^^ + | + = help: consider removing the parameters + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:8:23 + | +LL | fn unused_with_lt<'a, T>(x: &'a u8) {} + | ^ + | + = help: consider removing the parameter + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:18:19 + | +LL | fn unused_bounded(x: U) {} + | ^^^^^^^^^^^ + | + = help: consider removing the parameter + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:20:24 + | +LL | fn unused_where_clause(x: U) + | ^^ + | + = help: consider removing the parameter + +error: type parameters go unused in function definition + --> $DIR/extra_unused_type_parameters.rs:26:16 + | +LL | fn some_unused, E>(b: B, c: C) {} + | ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ + | + = help: consider removing the parameters + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:49:22 + | +LL | fn unused_ty_impl(&self) {} + | ^^^ + | + = help: consider removing the parameter + +error: aborting due to 7 previous errors + diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index 78397c2af34..b5dec6c46bd 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -282,4 +282,50 @@ impl AsyncLen { } } +// issue #9520 +pub struct NonStandardLenAndIsEmptySignature; +impl NonStandardLenAndIsEmptySignature { + // don't lint + pub fn len(&self, something: usize) -> usize { + something + } + + pub fn is_empty(&self, something: usize) -> bool { + something == 0 + } +} + +// test case for #9520 with generics in the function signature +pub trait TestResource { + type NonStandardSignatureWithGenerics: Copy; + fn lookup_content(&self, item: Self::NonStandardSignatureWithGenerics) -> Result, String>; +} +pub struct NonStandardSignatureWithGenerics(u32); +impl NonStandardSignatureWithGenerics { + pub fn is_empty(self, resource: &T) -> bool + where + T: TestResource, + U: Copy + From, + { + if let Ok(Some(content)) = resource.lookup_content(self.into()) { + content.is_empty() + } else { + true + } + } + + // test case for #9520 with generics in the function signature + pub fn len(self, resource: &T) -> usize + where + T: TestResource, + U: Copy + From, + { + if let Ok(Some(content)) = resource.lookup_content(self.into()) { + content.len() + } else { + 0_usize + } + } +} + fn main() {} diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed index 638320dd6ee..8c7e919bf62 100644 --- a/tests/ui/manual_assert.edition2018.fixed +++ b/tests/ui/manual_assert.edition2018.fixed @@ -29,9 +29,7 @@ fn main() { panic!("qaqaq{:?}", a); } assert!(a.is_empty(), "qaqaq{:?}", a); - if !a.is_empty() { - panic!("qwqwq"); - } + assert!(a.is_empty(), "qwqwq"); if a.len() == 3 { println!("qwq"); println!("qwq"); @@ -46,21 +44,11 @@ fn main() { println!("qwq"); } let b = vec![1, 2, 3]; - if b.is_empty() { - panic!("panic1"); - } - if b.is_empty() && a.is_empty() { - panic!("panic2"); - } - if a.is_empty() && !b.is_empty() { - panic!("panic3"); - } - if b.is_empty() || a.is_empty() { - panic!("panic4"); - } - if a.is_empty() || !b.is_empty() { - panic!("panic5"); - } + assert!(!b.is_empty(), "panic1"); + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); if a.is_empty() { let _ = 0; @@ -71,12 +59,11 @@ fn main() { fn issue7730(a: u8) { // Suggestion should preserve comment - if a > 2 { - // comment - /* this is a + // comment +/* this is a multiline comment */ - /// Doc comment - panic!("panic with comment") // comment after `panic!` - } +/// Doc comment +// comment after `panic!` +assert!(!(a > 2), "panic with comment"); } diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 1f2e1e3087b..3555ac29243 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -8,6 +8,54 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:34:5 + | +LL | / if !a.is_empty() { +LL | | panic!("qwqwq"); +LL | | } + | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:51:5 + | +LL | / if b.is_empty() { +LL | | panic!("panic1"); +LL | | } + | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:54:5 + | +LL | / if b.is_empty() && a.is_empty() { +LL | | panic!("panic2"); +LL | | } + | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:57:5 + | +LL | / if a.is_empty() && !b.is_empty() { +LL | | panic!("panic3"); +LL | | } + | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:60:5 + | +LL | / if b.is_empty() || a.is_empty() { +LL | | panic!("panic4"); +LL | | } + | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:63:5 + | +LL | / if a.is_empty() || !b.is_empty() { +LL | | panic!("panic5"); +LL | | } + | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` + error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 | @@ -16,5 +64,22 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` -error: aborting due to 2 previous errors +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:78:5 + | +LL | / if a > 2 { +LL | | // comment +LL | | /* this is a +LL | | multiline +... | +LL | | panic!("panic with comment") // comment after `panic!` +LL | | } + | |_____^ + | +help: try instead + | +LL | assert!(!(a > 2), "panic with comment"); + | + +error: aborting due to 9 previous errors diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index fc252cdd352..9fd3739b69c 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -123,7 +123,7 @@ fn main() { Enum::A => (), Enum::B => (), Enum::C => (), - _ => (), + Enum::__Private => (), } match Enum::A { Enum::A => (), diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 6fa313dc911..105b4c4b41d 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -48,11 +48,17 @@ error: wildcard matches only a single variant and will also match any future add LL | _ => (), | ^ help: try this: `Color::Blue` +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:126:13 + | +LL | _ => (), + | ^ help: try this: `Enum::__Private` + error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:153:13 | LL | _ => 2, | ^ help: try this: `Foo::B` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index 41263535df6..4511bc99c3c 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,9 +1,13 @@ +// aux-build:macro_rules.rs #![allow(unused)] #![allow(deref_nullptr)] #![allow(clippy::unnecessary_operation)] #![allow(clippy::drop_copy)] #![warn(clippy::multiple_unsafe_ops_per_block)] +#[macro_use] +extern crate macro_rules; + use core::arch::asm; fn raw_ptr() -> *const () { @@ -107,4 +111,9 @@ unsafe fn read_char_good(ptr: *const u8) -> char { unsafe { core::char::from_u32_unchecked(int_value) } } +// no lint +fn issue10259() { + unsafe_macro!(); +} + fn main() {} diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr index f6b8341795d..303aeb7aee0 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -1,5 +1,5 @@ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:32:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:36:5 | LL | / unsafe { LL | | STATIC += 1; @@ -8,19 +8,19 @@ LL | | } | |_____^ | note: modification of a mutable static occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:33:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:37:9 | LL | STATIC += 1; | ^^^^^^^^^^^ note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:34:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:38:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ = note: `-D clippy::multiple-unsafe-ops-per-block` implied by `-D warnings` error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:41:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:45:5 | LL | / unsafe { LL | | drop(u.u); @@ -29,18 +29,18 @@ LL | | } | |_____^ | note: union field access occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:42:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:46:14 | LL | drop(u.u); | ^^^ note: raw pointer dereference occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:43:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:47:9 | LL | *raw_ptr(); | ^^^^^^^^^^ error: this `unsafe` block contains 3 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:48:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:52:5 | LL | / unsafe { LL | | asm!("nop"); @@ -50,23 +50,23 @@ LL | | } | |_____^ | note: inline assembly used here - --> $DIR/multiple_unsafe_ops_per_block.rs:49:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:53:9 | LL | asm!("nop"); | ^^^^^^^^^^^ note: unsafe method call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:50:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:54:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: modification of a mutable static occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:51:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:55:9 | LL | STATIC = 0; | ^^^^^^^^^^ error: this `unsafe` block contains 6 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:57:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:61:5 | LL | / unsafe { LL | | drop(u.u); @@ -78,49 +78,49 @@ LL | | } | |_____^ | note: union field access occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:58:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:62:14 | LL | drop(u.u); | ^^^ note: access of a mutable static occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:59:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:63:14 | LL | drop(STATIC); | ^^^^^^ note: unsafe method call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:60:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:64:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:61:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:65:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:62:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:66:9 | LL | *raw_ptr(); | ^^^^^^^^^^ note: inline assembly used here - --> $DIR/multiple_unsafe_ops_per_block.rs:63:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:67:9 | LL | asm!("nop"); | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:101:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:105:5 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:101:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:105:14 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:101:39 + --> $DIR/multiple_unsafe_ops_per_block.rs:105:39 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed new file mode 100644 index 00000000000..d286ef4ba37 --- /dev/null +++ b/tests/ui/needless_lifetimes.fixed @@ -0,0 +1,538 @@ +// run-rustfix +// aux-build:macro_rules.rs + +#![warn(clippy::needless_lifetimes)] +#![allow( + unused, + clippy::boxed_local, + clippy::extra_unused_type_parameters, + clippy::needless_pass_by_value, + clippy::unnecessary_wraps, + dyn_drop, + clippy::get_first +)] + +#[macro_use] +extern crate macro_rules; + +fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} + +fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} + +// No error; same lifetime on two params. +fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {} + +// No error; static involved. +fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {} + +fn mut_and_static_input(_x: &mut u8, _y: &'static str) {} + +fn in_and_out(x: &u8, _y: u8) -> &u8 { + x +} + +// No error; multiple input refs. +fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 +// ^^^ +fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 +// ^^^ +fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { + y +} + +// No error; multiple input refs +async fn func<'a>(args: &[&'a str]) -> Option<&'a str> { + args.get(0).cloned() +} + +// No error; static involved. +fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> +// ^^^ +fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { + Ok(x) +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> +// ^^^ +fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { + Ok(y) +} + +// No error; two input refs. +fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 { + x.unwrap() +} + +fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { + Ok(x) +} + +// Where-clause, but without lifetimes. +fn where_clause_without_lt(x: &u8, _y: u8) -> Result<&u8, ()> +where + T: Copy, +{ + Ok(x) +} + +type Ref<'r> = &'r u8; + +// No error; same lifetime on two params. +fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {} + +fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} + +// No error; bounded lifetime. +fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {} + +// No error; bounded lifetime. +fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8) +where + 'b: 'a, +{ +} + +struct Lt<'a, I: 'static> { + x: &'a I, +} + +// No error; fn bound references `'a`. +fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +where + F: Fn(Lt<'a, I>) -> Lt<'a, I>, +{ + unreachable!() +} + +fn fn_bound_2(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> +where + for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>, +{ + unreachable!() +} + +// No error; see below. +fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) { + f(x); +} + +fn fn_bound_3_cannot_elide() { + let x = 42; + let p = &x; + let mut q = &x; + // This will fail if we elide lifetimes of `fn_bound_3`. + fn_bound_3(p, |y| q = y); +} + +// No error; multiple input refs. +fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () { + if cond { x } else { f() } +} + +struct X { + x: u8, +} + +impl X { + fn self_and_out(&self) -> &u8 { + &self.x + } + + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 + // ^^^ + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { + &self.x + } + + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 + // ^^^^^ + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { + x + } + + fn distinct_self_and_in(&self, _x: &u8) {} + + // No error; same lifetimes on two params. + fn self_and_same_in<'s>(&'s self, _x: &'s u8) {} +} + +struct Foo<'a>(&'a u8); + +impl<'a> Foo<'a> { + // No error; lifetime `'a` not defined in method. + fn self_shared_lifetime(&self, _: &'a u8) {} + // No error; bounds exist. + fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {} +} + +fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 { + unimplemented!() +} + +fn struct_with_lt(_foo: Foo<'_>) -> &str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `Foo`). +fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `Foo`). +fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str +// ^^ +fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str +// ^^^^ +fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { + unimplemented!() +} + +trait WithLifetime<'a> {} + +type WithLifetimeAlias<'a> = dyn WithLifetime<'a>; + +// Should not warn because it won't build without the lifetime. +fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str { + unimplemented!() +} + +// Should warn because there is no lifetime on `Drop`, so this would be +// unambiguous if we elided the lifetime. +fn trait_obj_elided2(_arg: &dyn Drop) -> &str { + unimplemented!() +} + +type FooAlias<'a> = Foo<'a>; + +fn alias_with_lt(_foo: FooAlias<'_>) -> &str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`). +fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`). +fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str +// ^^ +fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str +// ^^^^^^^^^ +fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { + unimplemented!() +} + +fn named_input_elided_output(_arg: &str) -> &str { + unimplemented!() +} + +fn elided_input_named_output<'a>(_arg: &str) -> &'a str { + unimplemented!() +} + +fn trait_bound_ok>(_: &u8, _: T) { + unimplemented!() +} +fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) { + unimplemented!() +} + +// Don't warn on these; see issue #292. +fn trait_bound_bug<'a, T: WithLifetime<'a>>() { + unimplemented!() +} + +// See issue #740. +struct Test { + vec: Vec, +} + +impl Test { + fn iter<'a>(&'a self) -> Box + 'a> { + unimplemented!() + } +} + +trait LintContext<'a> {} + +fn f<'a, T: LintContext<'a>>(_: &T) {} + +fn test<'a>(x: &'a [u8]) -> u8 { + let y: &'a u8 = &x[5]; + *y +} + +// Issue #3284: give hint regarding lifetime in return type. +struct Cow<'a> { + x: &'a str, +} +fn out_return_type_lts(e: &str) -> Cow<'_> { + unimplemented!() +} + +// Make sure we still warn on implementations +mod issue4291 { + trait BadTrait { + fn needless_lt(x: &u8) {} + } + + impl BadTrait for () { + fn needless_lt(_x: &u8) {} + } +} + +mod issue2944 { + trait Foo {} + struct Bar; + struct Baz<'a> { + bar: &'a Bar, + } + + impl<'a> Foo for Baz<'a> {} + impl Bar { + fn baz(&self) -> impl Foo + '_ { + Baz { bar: self } + } + } +} + +mod nested_elision_sites { + // issue #issue2944 + + // closure trait bounds subject to nested elision + // don't lint because they refer to outer lifetimes + fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + move || i + } + fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { + move || i + } + + // don't lint + fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 { + f() + } + fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + // lint + fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + f(i) + } + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 { + f(i) + } + + // don't lint + fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + f() + } + // lint + fn generics_elidable &i32>(i: &i32, f: T) -> &i32 { + f(i) + } + + // don't lint + fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32 + where + T: Fn() -> &'a i32, + { + f() + } + // lint + fn where_clause_elidadable(i: &i32, f: T) -> &i32 + where + T: Fn(&i32) -> &i32, + { + f(i) + } + + // don't lint + fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 { + f(i) + } + fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { + |i| i + } + // lint + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { + f(i) + } + + // don't lint + fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) { + |f| () + } + + // lint + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { + |f| () + } +} + +mod issue6159 { + use std::ops::Deref; + pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R + where + T: Deref, + F: FnOnce(&'a T::Target) -> R, + { + f(x.deref()) + } +} + +mod issue7296 { + use std::rc::Rc; + use std::sync::Arc; + + struct Foo; + impl Foo { + fn implicit(&self) -> &() { + &() + } + fn implicit_mut(&mut self) -> &() { + &() + } + + fn explicit<'a>(self: &'a Arc) -> &'a () { + &() + } + fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { + &() + } + + fn lifetime_elsewhere(self: Box, here: &()) -> &() { + &() + } + } + + trait Bar { + fn implicit(&self) -> &(); + fn implicit_provided(&self) -> &() { + &() + } + + fn explicit<'a>(self: &'a Arc) -> &'a (); + fn explicit_provided<'a>(self: &'a Arc) -> &'a () { + &() + } + + fn lifetime_elsewhere(self: Box, here: &()) -> &(); + fn lifetime_elsewhere_provided(self: Box, here: &()) -> &() { + &() + } + } +} + +mod pr_9743_false_negative_fix { + #![allow(unused)] + + fn foo(x: &u8, y: &'_ u8) {} + + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} +} + +mod pr_9743_output_lifetime_checks { + #![allow(unused)] + + // lint: only one input + fn one_input(x: &u8) -> &u8 { + unimplemented!() + } + + // lint: multiple inputs, output would not be elided + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 { + unimplemented!() + } + + // don't lint: multiple inputs, output would be elided (which would create an ambiguity) + fn multiple_inputs_output_would_be_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'a u8 { + unimplemented!() + } +} + +mod in_macro { + macro_rules! local_one_input_macro { + () => { + fn one_input(x: &u8) -> &u8 { + unimplemented!() + } + }; + } + + // lint local macro expands to function with needless lifetimes + local_one_input_macro!(); + + // no lint on external macro + macro_rules::needless_lifetime!(); +} + +mod issue5787 { + use std::sync::MutexGuard; + + struct Foo; + + impl Foo { + // doesn't get linted without async + pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + guard + } + } + + async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str { + y + } +} + +fn main() {} diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 2efc936752e..409528b291d 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -1,13 +1,20 @@ +// run-rustfix +// aux-build:macro_rules.rs + #![warn(clippy::needless_lifetimes)] #![allow( - dead_code, + unused, clippy::boxed_local, + clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, clippy::unnecessary_wraps, dyn_drop, clippy::get_first )] +#[macro_use] +extern crate macro_rules; + fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} @@ -495,4 +502,37 @@ mod pr_9743_output_lifetime_checks { } } +mod in_macro { + macro_rules! local_one_input_macro { + () => { + fn one_input<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + }; + } + + // lint local macro expands to function with needless lifetimes + local_one_input_macro!(); + + // no lint on external macro + macro_rules::needless_lifetime!(); +} + +mod issue5787 { + use std::sync::MutexGuard; + + struct Foo; + + impl Foo { + // doesn't get linted without async + pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + guard + } + } + + async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str { + y + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index 5a7cf13c86d..4e3c8f20d8c 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -1,316 +1,559 @@ error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:11:1 + --> $DIR/needless_lifetimes.rs:18:1 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` +help: elide the lifetimes + | +LL - fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} +LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} + | error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:13:1 + --> $DIR/needless_lifetimes.rs:20:1 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} +LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:23:1 + --> $DIR/needless_lifetimes.rs:30:1 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { +LL + fn in_and_out(x: &u8, _y: u8) -> &u8 { + | error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:35:1 + --> $DIR/needless_lifetimes.rs:42:1 | LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { +LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:42:1 + --> $DIR/needless_lifetimes.rs:49:1 | LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { +LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { + | error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:59:1 + --> $DIR/needless_lifetimes.rs:66:1 | LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { +LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:66:1 + --> $DIR/needless_lifetimes.rs:73:1 | LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { +LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:75:1 + --> $DIR/needless_lifetimes.rs:82:1 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { +LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:80:1 + --> $DIR/needless_lifetimes.rs:87:1 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> +LL + fn where_clause_without_lt(x: &u8, _y: u8) -> Result<&u8, ()> + | error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:92:1 + --> $DIR/needless_lifetimes.rs:99:1 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:92:37 +help: elide the lifetimes + | +LL - fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} +LL + fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} | -LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:116:1 + --> $DIR/needless_lifetimes.rs:123:1 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:116:32 +help: elide the lifetimes + | +LL - fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +LL + fn fn_bound_2(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> | -LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> - | ^^ error: the following explicit lifetimes could be elided: 's - --> $DIR/needless_lifetimes.rs:146:5 + --> $DIR/needless_lifetimes.rs:153:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_out<'s>(&'s self) -> &'s u8 { +LL + fn self_and_out(&self) -> &u8 { + | error: the following explicit lifetimes could be elided: 't - --> $DIR/needless_lifetimes.rs:153:5 + --> $DIR/needless_lifetimes.rs:160:5 | LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { +LL + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { + | error: the following explicit lifetimes could be elided: 's - --> $DIR/needless_lifetimes.rs:160:5 + --> $DIR/needless_lifetimes.rs:167:5 | LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { +LL + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { + | error: the following explicit lifetimes could be elided: 's, 't - --> $DIR/needless_lifetimes.rs:164:5 + --> $DIR/needless_lifetimes.rs:171:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} +LL + fn distinct_self_and_in(&self, _x: &u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:183:1 + --> $DIR/needless_lifetimes.rs:190:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:183:33 +help: elide the lifetimes + | +LL - fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { +LL + fn struct_with_lt(_foo: Foo<'_>) -> &str { | -LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:201:1 + --> $DIR/needless_lifetimes.rs:208:1 | LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:201:43 +help: elide the lifetimes + | +LL - fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { +LL + fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { | -LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:209:1 + --> $DIR/needless_lifetimes.rs:216:1 | LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { +LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:224:1 + --> $DIR/needless_lifetimes.rs:231:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { +LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:230:1 + --> $DIR/needless_lifetimes.rs:237:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:230:37 +help: elide the lifetimes + | +LL - fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { +LL + fn alias_with_lt(_foo: FooAlias<'_>) -> &str { | -LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:248:1 + --> $DIR/needless_lifetimes.rs:255:1 | LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:248:47 +help: elide the lifetimes + | +LL - fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { +LL + fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { | -LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:256:1 + --> $DIR/needless_lifetimes.rs:263:1 | LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { +LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:260:1 + --> $DIR/needless_lifetimes.rs:267:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn named_input_elided_output<'a>(_arg: &'a str) -> &str { +LL + fn named_input_elided_output(_arg: &str) -> &str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:268:1 + --> $DIR/needless_lifetimes.rs:275:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { +LL + fn trait_bound_ok>(_: &u8, _: T) { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:304:1 + --> $DIR/needless_lifetimes.rs:311:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:304:47 +help: elide the lifetimes + | +LL - fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { +LL + fn out_return_type_lts(e: &str) -> Cow<'_> { | -LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:311:9 + --> $DIR/needless_lifetimes.rs:318:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn needless_lt<'a>(x: &'a u8) {} +LL + fn needless_lt(x: &u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:315:9 + --> $DIR/needless_lifetimes.rs:322:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn needless_lt<'a>(_x: &'a u8) {} +LL + fn needless_lt(_x: &u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:328:9 + --> $DIR/needless_lifetimes.rs:335:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn baz<'a>(&'a self) -> impl Foo + 'a { +LL + fn baz(&self) -> impl Foo + '_ { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:360:5 + --> $DIR/needless_lifetimes.rs:367:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { +LL + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:369:5 + --> $DIR/needless_lifetimes.rs:376:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { +LL + fn generics_elidable &i32>(i: &i32, f: T) -> &i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:381:5 + --> $DIR/needless_lifetimes.rs:388:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 +LL + fn where_clause_elidadable(i: &i32, f: T) -> &i32 + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:396:5 + --> $DIR/needless_lifetimes.rs:403:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { +LL + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:409:5 + --> $DIR/needless_lifetimes.rs:416:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { +LL + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:412:5 + --> $DIR/needless_lifetimes.rs:419:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { +LL + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:434:9 + --> $DIR/needless_lifetimes.rs:441:9 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit<'a>(&'a self) -> &'a () { +LL + fn implicit(&self) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:437:9 + --> $DIR/needless_lifetimes.rs:444:9 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:448:9 | -LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:454:9 +help: elide the lifetimes + | +LL - fn implicit_mut<'a>(&'a mut self) -> &'a () { +LL + fn implicit_mut(&mut self) -> &() { | -LL | fn implicit<'a>(&'a self) -> &'a (); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a --> $DIR/needless_lifetimes.rs:455:9 | -LL | fn implicit_provided<'a>(&'a self) -> &'a () { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { +LL + fn lifetime_elsewhere(self: Box, here: &()) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:464:9 + --> $DIR/needless_lifetimes.rs:461:9 + | +LL | fn implicit<'a>(&'a self) -> &'a (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit<'a>(&'a self) -> &'a (); +LL + fn implicit(&self) -> &(); + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:462:9 + | +LL | fn implicit_provided<'a>(&'a self) -> &'a () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit_provided<'a>(&'a self) -> &'a () { +LL + fn implicit_provided(&self) -> &() { + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:471:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); +LL + fn lifetime_elsewhere(self: Box, here: &()) -> &(); + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:465:9 + --> $DIR/needless_lifetimes.rs:472:9 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { +LL + fn lifetime_elsewhere_provided(self: Box, here: &()) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:474:5 + --> $DIR/needless_lifetimes.rs:481:5 | LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:476:5 | -LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: elide the lifetimes + | +LL - fn foo<'a>(x: &'a u8, y: &'_ u8) {} +LL + fn foo(x: &u8, y: &'_ u8) {} + | error: the following explicit lifetimes could be elided: 'a --> $DIR/needless_lifetimes.rs:483:5 | -LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} +LL + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:488:5 + --> $DIR/needless_lifetimes.rs:490:5 + | +LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { +LL + fn one_input(x: &u8) -> &u8 { + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:495:5 | LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { +LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 { + | -error: aborting due to 45 previous errors +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:508:13 + | +LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | local_one_input_macro!(); + | ------------------------ in this macro invocation + | + = note: this error originates in the macro `local_one_input_macro` (in Nightly builds, run with -Z macro-backtrace for more info) +help: elide the lifetimes + | +LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { +LL + fn one_input(x: &u8) -> &u8 { + | + +error: aborting due to 46 previous errors diff --git a/tests/ui/needless_range_loop.stderr b/tests/ui/needless_range_loop.stderr index b31544ec334..cffa19bec3a 100644 --- a/tests/ui/needless_range_loop.stderr +++ b/tests/ui/needless_range_loop.stderr @@ -49,7 +49,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, ) in vec.iter().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ @@ -126,7 +126,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, ) in vec.iter().enumerate().skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -137,7 +137,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 5..10 { | ^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, ) in vec.iter().enumerate().take(10).skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -148,7 +148,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, ) in vec.iter_mut().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index 65809023f8d..7803418cb04 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -1,4 +1,9 @@ -#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)] +#![allow( + dead_code, + clippy::missing_safety_doc, + clippy::extra_unused_lifetimes, + clippy::extra_unused_type_parameters +)] #![warn(clippy::new_without_default)] pub struct Foo; diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index 212a69ab94e..583dd327d6a 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -1,5 +1,5 @@ error: you should consider adding a `Default` implementation for `Foo` - --> $DIR/new_without_default.rs:7:5 + --> $DIR/new_without_default.rs:12:5 | LL | / pub fn new() -> Foo { LL | | Foo @@ -17,7 +17,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Bar` - --> $DIR/new_without_default.rs:15:5 + --> $DIR/new_without_default.rs:20:5 | LL | / pub fn new() -> Self { LL | | Bar @@ -34,7 +34,7 @@ LL + } | error: you should consider adding a `Default` implementation for `LtKo<'c>` - --> $DIR/new_without_default.rs:79:5 + --> $DIR/new_without_default.rs:84:5 | LL | / pub fn new() -> LtKo<'c> { LL | | unimplemented!() @@ -51,7 +51,7 @@ LL + } | error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` - --> $DIR/new_without_default.rs:172:5 + --> $DIR/new_without_default.rs:177:5 | LL | / pub fn new() -> Self { LL | | NewNotEqualToDerive { foo: 1 } @@ -68,7 +68,7 @@ LL + } | error: you should consider adding a `Default` implementation for `FooGenerics` - --> $DIR/new_without_default.rs:180:5 + --> $DIR/new_without_default.rs:185:5 | LL | / pub fn new() -> Self { LL | | Self(Default::default()) @@ -85,7 +85,7 @@ LL + } | error: you should consider adding a `Default` implementation for `BarGenerics` - --> $DIR/new_without_default.rs:187:5 + --> $DIR/new_without_default.rs:192:5 | LL | / pub fn new() -> Self { LL | | Self(Default::default()) @@ -102,7 +102,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Foo` - --> $DIR/new_without_default.rs:198:9 + --> $DIR/new_without_default.rs:203:9 | LL | / pub fn new() -> Self { LL | | todo!() diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index ec7f8ae923a..276266a2dd8 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::redundant_field_names)] -#![allow(clippy::no_effect, dead_code, unused_variables)] +#![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] extern crate derive_new; diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index 73122016cf6..f674141c138 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::redundant_field_names)] -#![allow(clippy::no_effect, dead_code, unused_variables)] +#![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] extern crate derive_new; diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index f0e1a8128d7..ab8ac97a0e7 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -36,6 +36,10 @@ fn syntax_error() { let raw_string_error = Regex::new(r"[...\/...]"); let raw_string_error = Regex::new(r#"[...\/...]"#); + + let escaped_string_span = Regex::new("\\b\\c"); + + let aux_span = Regex::new("(?ixi)"); } fn trivial_regex() { diff --git a/tests/ui/regex.stderr b/tests/ui/regex.stderr index 2424644c6f6..c2440f39e0a 100644 --- a/tests/ui/regex.stderr +++ b/tests/ui/regex.stderr @@ -29,7 +29,10 @@ error: regex syntax error: invalid character class range, the start must be <= t LL | let some_unicode = Regex::new("[é-è]"); | ^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:18:33 | LL | let some_regex = Regex::new(OPENING_PAREN); @@ -43,25 +46,37 @@ LL | let binary_pipe_in_wrong_position = BRegex::new("|"); | = help: the regex is unlikely to be useful as it is -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:21:41 | LL | let some_binary_regex = BRegex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:22:56 | LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:34:37 | LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:35:39 | LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); @@ -79,8 +94,25 @@ error: regex syntax error: unrecognized escape sequence LL | let raw_string_error = Regex::new(r#"[...//...]"#); | ^^ +error: regex parse error: + /b/c + ^^ + error: unrecognized escape sequence + --> $DIR/regex.rs:40:42 + | +LL | let escaped_string_span = Regex::new("/b/c"); + | ^^^^^^^^ + | + = help: consider using a raw string literal: `r".."` + +error: regex syntax error: duplicate flag + --> $DIR/regex.rs:42:34 + | +LL | let aux_span = Regex::new("(?ixi)"); + | ^ ^ + error: trivial regex - --> $DIR/regex.rs:42:33 + --> $DIR/regex.rs:46:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -88,7 +120,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:44:48 + --> $DIR/regex.rs:48:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:46:42 + --> $DIR/regex.rs:50:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -104,7 +136,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> $DIR/regex.rs:48:40 + --> $DIR/regex.rs:52:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -112,7 +144,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> $DIR/regex.rs:50:39 + --> $DIR/regex.rs:54:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -120,7 +152,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:52:39 + --> $DIR/regex.rs:56:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -128,7 +160,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:54:40 + --> $DIR/regex.rs:58:40 | LL | let trivial_backslash = Regex::new("a/.b"); | ^^^^^^^ @@ -136,7 +168,7 @@ LL | let trivial_backslash = Regex::new("a/.b"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:57:36 + --> $DIR/regex.rs:61:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -144,7 +176,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:59:36 + --> $DIR/regex.rs:63:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -152,7 +184,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:61:36 + --> $DIR/regex.rs:65:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -160,12 +192,12 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> $DIR/regex.rs:63:44 + --> $DIR/regex.rs:67:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ | = help: consider using `str::is_empty` -error: aborting due to 23 previous errors +error: aborting due to 25 previous errors diff --git a/tests/ui/seek_to_start_instead_of_rewind.fixed b/tests/ui/seek_to_start_instead_of_rewind.fixed index 713cff604a1..dc24d447c60 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.fixed +++ b/tests/ui/seek_to_start_instead_of_rewind.fixed @@ -26,7 +26,7 @@ fn seek_to_start_false_method(t: &mut StructWithSeekMethod) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { +fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { t.seek(SeekFrom::Start(0)); } @@ -38,7 +38,7 @@ fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { +fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { t.seek(SeekFrom::Start(0)); } diff --git a/tests/ui/seek_to_start_instead_of_rewind.rs b/tests/ui/seek_to_start_instead_of_rewind.rs index 467003a1a66..4adde2c4018 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.rs +++ b/tests/ui/seek_to_start_instead_of_rewind.rs @@ -26,7 +26,7 @@ fn seek_to_start_false_method(t: &mut StructWithSeekMethod) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { +fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { t.seek(SeekFrom::Start(0)); } @@ -38,7 +38,7 @@ fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { +fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { t.seek(SeekFrom::Start(0)); } diff --git a/tests/ui/suspicious_to_owned.stderr b/tests/ui/suspicious_to_owned.stderr index dec3f50d6f1..c4ec7aa88a2 100644 --- a/tests/ui/suspicious_to_owned.stderr +++ b/tests/ui/suspicious_to_owned.stderr @@ -2,27 +2,62 @@ error: this `to_owned` call clones the Cow<'_, str> itself and does not cause th --> $DIR/suspicious_to_owned.rs:16:13 | LL | let _ = cow.to_owned(); - | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | ^^^^^^^^^^^^^^ | = note: `-D clippy::suspicious-to-owned` implied by `-D warnings` +help: depending on intent, either make the Cow an Owned variant + | +LL | let _ = cow.into_owned(); + | ~~~~~~~~~~~~~~~~ +help: or clone the Cow itself + | +LL | let _ = cow.clone(); + | ~~~~~~~~~~~ error: this `to_owned` call clones the Cow<'_, [char; 3]> itself and does not cause the Cow<'_, [char; 3]> contents to become owned --> $DIR/suspicious_to_owned.rs:26:13 | LL | let _ = cow.to_owned(); - | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | ^^^^^^^^^^^^^^ + | +help: depending on intent, either make the Cow an Owned variant + | +LL | let _ = cow.into_owned(); + | ~~~~~~~~~~~~~~~~ +help: or clone the Cow itself + | +LL | let _ = cow.clone(); + | ~~~~~~~~~~~ error: this `to_owned` call clones the Cow<'_, Vec> itself and does not cause the Cow<'_, Vec> contents to become owned --> $DIR/suspicious_to_owned.rs:36:13 | LL | let _ = cow.to_owned(); - | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | ^^^^^^^^^^^^^^ + | +help: depending on intent, either make the Cow an Owned variant + | +LL | let _ = cow.into_owned(); + | ~~~~~~~~~~~~~~~~ +help: or clone the Cow itself + | +LL | let _ = cow.clone(); + | ~~~~~~~~~~~ error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned --> $DIR/suspicious_to_owned.rs:46:13 | LL | let _ = cow.to_owned(); - | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | ^^^^^^^^^^^^^^ + | +help: depending on intent, either make the Cow an Owned variant + | +LL | let _ = cow.into_owned(); + | ~~~~~~~~~~~~~~~~ +help: or clone the Cow itself + | +LL | let _ = cow.clone(); + | ~~~~~~~~~~~ error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type --> $DIR/suspicious_to_owned.rs:60:13 diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index 2eca1f4701c..8b4613b3f6e 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -1,4 +1,5 @@ #![deny(clippy::type_repetition_in_bounds)] +#![allow(clippy::extra_unused_type_parameters)] use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 70d700c1cc4..a90df03c04f 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -1,5 +1,5 @@ error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:8:5 + --> $DIR/type_repetition_in_bounds.rs:9:5 | LL | T: Clone, | ^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:25:5 + --> $DIR/type_repetition_in_bounds.rs:26:5 | LL | Self: Copy + Default + Ord, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | Self: Copy + Default + Ord, = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:85:5 + --> $DIR/type_repetition_in_bounds.rs:86:5 | LL | T: Clone, | ^^^^^^^^ @@ -28,7 +28,7 @@ LL | T: Clone, = help: consider combining the bounds: `T: ?Sized + Clone` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:90:5 + --> $DIR/type_repetition_in_bounds.rs:91:5 | LL | T: ?Sized, | ^^^^^^^^^ diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index 4b059558173..8d3e094b759 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -63,6 +63,14 @@ fn combine_or(file: &str) -> Result<(), Error> { Ok(()) } +fn is_ok_err(s: &mut T) { + s.write(b"ok").is_ok(); + s.write(b"err").is_err(); + let mut buf = [0u8; 0]; + s.read(&mut buf).is_ok(); + s.read(&mut buf).is_err(); +} + async fn bad_async_write(w: &mut W) { w.write(b"hello world").await.unwrap(); } diff --git a/tests/ui/unused_io_amount.stderr b/tests/ui/unused_io_amount.stderr index 7ba7e09c0f0..0865c5213f6 100644 --- a/tests/ui/unused_io_amount.stderr +++ b/tests/ui/unused_io_amount.stderr @@ -82,13 +82,45 @@ LL | | .expect("error"); error: written amount is not handled --> $DIR/unused_io_amount.rs:67:5 | +LL | s.write(b"ok").is_ok(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:68:5 + | +LL | s.write(b"err").is_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:70:5 + | +LL | s.read(&mut buf).is_ok(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:71:5 + | +LL | s.read(&mut buf).is_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:75:5 + | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:72:5 + --> $DIR/unused_io_amount.rs:80:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | r.read(&mut buf[..]).await.unwrap(); = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:85:9 + --> $DIR/unused_io_amount.rs:93:9 | LL | w.write(b"hello world").await?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +136,7 @@ LL | w.write(b"hello world").await?; = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:93:9 + --> $DIR/unused_io_amount.rs:101:9 | LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +144,7 @@ LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:101:5 + --> $DIR/unused_io_amount.rs:109:5 | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,12 +152,12 @@ LL | w.write(b"hello world").await.unwrap(); = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:106:5 + --> $DIR/unused_io_amount.rs:114:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `AsyncReadExt::read_exact` instead, or handle partial reads -error: aborting due to 16 previous errors +error: aborting due to 20 previous errors diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed index 23607497841..293bf75a717 100644 --- a/tests/ui/wildcard_enum_match_arm.fixed +++ b/tests/ui/wildcard_enum_match_arm.fixed @@ -96,7 +96,7 @@ fn main() { } match Enum::A { Enum::A => (), - Enum::B | _ => (), + Enum::B | Enum::__Private => (), } } } diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index efecc9576cc..30d29aa4e77 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -34,11 +34,11 @@ error: wildcard matches known variants and will also match future added variants LL | _ => {}, | ^ help: try this: `ErrorKind::PermissionDenied | _` -error: wildcard matches known variants and will also match future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:99:13 | LL | _ => (), - | ^ help: try this: `Enum::B | _` + | ^ help: try this: `Enum::B | Enum::__Private` error: aborting due to 6 previous errors