diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ef00fcbaf..6ef338b819d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3276,9 +3276,11 @@ Released 2018-09-13 [`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons [`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason +[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions +[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops @@ -3296,6 +3298,7 @@ Released 2018-09-13 [`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison [`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr +[`borrow_deref_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection @@ -3362,6 +3365,7 @@ Released 2018-09-13 [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression +[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use @@ -3435,6 +3439,7 @@ Released 2018-09-13 [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into [`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send +[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap [`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion @@ -3556,6 +3561,7 @@ Released 2018-09-13 [`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max [`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute [`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os +[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items @@ -3613,6 +3619,7 @@ Released 2018-09-13 [`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default [`new_without_default_derive`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default_derive [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect +[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace [`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions @@ -3729,7 +3736,6 @@ Released 2018-09-13 [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive -[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign @@ -3750,6 +3756,7 @@ Released 2018-09-13 [`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings [`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting +[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr @@ -3820,6 +3827,7 @@ Released 2018-09-13 [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label +[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings diff --git a/Cargo.toml b/Cargo.toml index 373e720b0d5..d23d681df00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ tempfile = { version = "3.2", optional = true } termize = "0.1" [dev-dependencies] -compiletest_rs = { version = "0.7.1", features = ["tmp"] } +compiletest_rs = { version = "0.8", features = ["tmp"] } tester = "0.9" regex = "1.5" # This is used by the `collect-metadata` alias. @@ -40,6 +40,7 @@ filetime = "0.2" rustc-workspace-hack = "1.0" # UI test dependencies +clap = { version = "3.1", features = ["derive"] } clippy_utils = { path = "clippy_utils" } derive-new = "0.5" if_chain = "1.0" @@ -48,7 +49,7 @@ quote = "1.0" serde = { version = "1.0.125", features = ["derive"] } syn = { version = "1.0", features = ["full"] } futures = "0.3" -parking_lot = "0.11.2" +parking_lot = "0.12" tokio = { version = "1", features = ["io-util"] } rustc-semver = "1.1" diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 2cfbcea5034..b0d470a2124 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] aho-corasick = "0.7" -clap = "2.33" +clap = "3.1" 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 d5cd7ca96c0..ee535b1d3be 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -2,20 +2,20 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; +use clap::{Arg, ArgMatches, Command}; use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints}; use indoc::indoc; fn main() { let matches = get_clap_config(); match matches.subcommand() { - ("bless", Some(matches)) => { + Some(("bless", matches)) => { bless::bless(matches.is_present("ignore-timestamp")); }, - ("fmt", Some(matches)) => { + Some(("fmt", matches)) => { fmt::run(matches.is_present("check"), matches.is_present("verbose")); }, - ("update_lints", Some(matches)) => { + Some(("update_lints", matches)) => { if matches.is_present("print-only") { update_lints::print_lints(); } else if matches.is_present("check") { @@ -24,7 +24,7 @@ fn main() { update_lints::update(update_lints::UpdateMode::Change); } }, - ("new_lint", Some(matches)) => { + Some(("new_lint", matches)) => { match new_lint::create( matches.value_of("pass"), matches.value_of("name"), @@ -35,8 +35,8 @@ fn main() { Err(e) => eprintln!("Unable to create lint: {}", e), } }, - ("setup", Some(sub_command)) => match sub_command.subcommand() { - ("intellij", Some(matches)) => { + Some(("setup", sub_command)) => match sub_command.subcommand() { + Some(("intellij", matches)) => { if matches.is_present("remove") { setup::intellij::remove_rustc_src(); } else { @@ -47,14 +47,14 @@ fn main() { ); } }, - ("git-hook", Some(matches)) => { + Some(("git-hook", matches)) => { if matches.is_present("remove") { setup::git_hook::remove_hook(); } else { setup::git_hook::install_hook(matches.is_present("force-override")); } }, - ("vscode-tasks", Some(matches)) => { + Some(("vscode-tasks", matches)) => { if matches.is_present("remove") { setup::vscode::remove_tasks(); } else { @@ -63,23 +63,23 @@ fn main() { }, _ => {}, }, - ("remove", Some(sub_command)) => match sub_command.subcommand() { - ("git-hook", Some(_)) => setup::git_hook::remove_hook(), - ("intellij", Some(_)) => setup::intellij::remove_rustc_src(), - ("vscode-tasks", Some(_)) => setup::vscode::remove_tasks(), + Some(("remove", sub_command)) => match sub_command.subcommand() { + Some(("git-hook", _)) => setup::git_hook::remove_hook(), + Some(("intellij", _)) => setup::intellij::remove_rustc_src(), + Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(), _ => {}, }, - ("serve", Some(matches)) => { + Some(("serve", matches)) => { let port = matches.value_of("port").unwrap().parse().unwrap(); let lint = matches.value_of("lint"); serve::run(port, lint); }, - ("lint", Some(matches)) => { + Some(("lint", matches)) => { let path = matches.value_of("path").unwrap(); let args = matches.values_of("args").into_iter().flatten(); lint::run(path, args); }, - ("rename_lint", Some(matches)) => { + Some(("rename_lint", matches)) => { let old_name = matches.value_of("old_name").unwrap(); let new_name = matches.value_of("new_name").unwrap_or(old_name); let uplift = matches.is_present("uplift"); @@ -89,35 +89,24 @@ fn main() { } } -fn get_clap_config<'a>() -> ArgMatches<'a> { - App::new("Clippy developer tooling") - .setting(AppSettings::ArgRequiredElseHelp) +fn get_clap_config() -> ArgMatches { + Command::new("Clippy developer tooling") + .arg_required_else_help(true) .subcommand( - SubCommand::with_name("bless") - .about("bless the test output changes") - .arg( - Arg::with_name("ignore-timestamp") - .long("ignore-timestamp") - .help("Include files updated before clippy was built"), - ), + Command::new("bless").about("bless the test output changes").arg( + Arg::new("ignore-timestamp") + .long("ignore-timestamp") + .help("Include files updated before clippy was built"), + ), ) .subcommand( - SubCommand::with_name("fmt") + Command::new("fmt") .about("Run rustfmt on all projects and tests") - .arg( - Arg::with_name("check") - .long("check") - .help("Use the rustfmt --check option"), - ) - .arg( - Arg::with_name("verbose") - .short("v") - .long("verbose") - .help("Echo commands run"), - ), + .arg(Arg::new("check").long("check").help("Use the rustfmt --check option")) + .arg(Arg::new("verbose").short('v').long("verbose").help("Echo commands run")), ) .subcommand( - SubCommand::with_name("update_lints") + Command::new("update_lints") .about("Updates lint registration and information from the source code") .long_about( "Makes sure that:\n \ @@ -127,23 +116,23 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ * all lints are registered in the lint store", ) - .arg(Arg::with_name("print-only").long("print-only").help( + .arg(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( - Arg::with_name("check") + Arg::new("check") .long("check") .help("Checks that `cargo dev update_lints` has been run. Used on CI."), ), ) .subcommand( - SubCommand::with_name("new_lint") + Command::new("new_lint") .about("Create new lint and run `cargo dev update_lints`") .arg( - Arg::with_name("pass") - .short("p") + Arg::new("pass") + .short('p') .long("pass") .help("Specify whether the lint runs during the early or late pass") .takes_value(true) @@ -151,16 +140,16 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .required(true), ) .arg( - Arg::with_name("name") - .short("n") + 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( - Arg::with_name("category") - .short("c") + Arg::new("category") + .short('c') .long("category") .help("What category the lint belongs to") .default_value("nursery") @@ -179,29 +168,25 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { ]) .takes_value(true), ) - .arg( - Arg::with_name("msrv") - .long("msrv") - .help("Add MSRV config code to the lint"), - ), + .arg(Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint")), ) .subcommand( - SubCommand::with_name("setup") + Command::new("setup") .about("Support for setting up your personal development environment") - .setting(AppSettings::ArgRequiredElseHelp) + .arg_required_else_help(true) .subcommand( - SubCommand::with_name("intellij") + Command::new("intellij") .about("Alter dependencies so Intellij Rust can find rustc internals") .arg( - Arg::with_name("remove") + Arg::new("remove") .long("remove") .help("Remove the dependencies added with 'cargo dev setup intellij'") .required(false), ) .arg( - Arg::with_name("rustc-repo-path") + Arg::new("rustc-repo-path") .long("repo-path") - .short("r") + .short('r') .help("The path to a rustc repo that will be used for setting the dependencies") .takes_value(true) .value_name("path") @@ -210,66 +195,65 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { ), ) .subcommand( - SubCommand::with_name("git-hook") + Command::new("git-hook") .about("Add a pre-commit git hook that formats your code to make it look pretty") .arg( - Arg::with_name("remove") + Arg::new("remove") .long("remove") .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") .required(false), ) .arg( - Arg::with_name("force-override") + Arg::new("force-override") .long("force-override") - .short("f") + .short('f') .help("Forces the override of an existing git pre-commit hook") .required(false), ), ) .subcommand( - SubCommand::with_name("vscode-tasks") + Command::new("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") .arg( - Arg::with_name("remove") + Arg::new("remove") .long("remove") .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") .required(false), ) .arg( - Arg::with_name("force-override") + Arg::new("force-override") .long("force-override") - .short("f") + .short('f') .help("Forces the override of existing vscode tasks") .required(false), ), ), ) .subcommand( - SubCommand::with_name("remove") + Command::new("remove") .about("Support for undoing changes done by the setup command") - .setting(AppSettings::ArgRequiredElseHelp) - .subcommand(SubCommand::with_name("git-hook").about("Remove any existing pre-commit git hook")) - .subcommand(SubCommand::with_name("vscode-tasks").about("Remove any existing vscode tasks")) + .arg_required_else_help(true) + .subcommand(Command::new("git-hook").about("Remove any existing pre-commit git hook")) + .subcommand(Command::new("vscode-tasks").about("Remove any existing vscode tasks")) .subcommand( - SubCommand::with_name("intellij") - .about("Removes rustc source paths added via `cargo dev setup intellij`"), + Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"), ), ) .subcommand( - SubCommand::with_name("serve") + Command::new("serve") .about("Launch a local 'ALL the Clippy Lints' website in a browser") .arg( - Arg::with_name("port") + Arg::new("port") .long("port") - .short("p") + .short('p') .help("Local port for the http server") .default_value("8000") .validator_os(serve::validate_port), ) - .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")), + .arg(Arg::new("lint").help("Which lint's page to load initially (optional)")), ) .subcommand( - SubCommand::with_name("lint") + Command::new("lint") .about("Manually run clippy on a file or package") .after_help(indoc! {" EXAMPLES @@ -288,33 +272,33 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { cargo dev lint ~/my-project -- -- -W clippy::pedantic "}) .arg( - Arg::with_name("path") + Arg::new("path") .required(true) .help("The path to a file or package directory to lint"), ) .arg( - Arg::with_name("args") - .multiple(true) + Arg::new("args") + .multiple_occurrences(true) .help("Pass extra arguments to cargo/clippy-driver"), ), ) .subcommand( - SubCommand::with_name("rename_lint") + Command::new("rename_lint") .about("Renames the given lint") .arg( - Arg::with_name("old_name") + Arg::new("old_name") .index(1) .required(true) .help("The name of the lint to rename"), ) .arg( - Arg::with_name("new_name") + Arg::new("new_name") .index(2) - .required_unless("uplift") + .required_unless_present("uplift") .help("The new name of the lint"), ) .arg( - Arg::with_name("uplift") + Arg::new("uplift") .long("uplift") .help("This lint will be uplifted into rustc"), ), diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index b36e2a28ee4..d55b1a354d0 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -1,4 +1,5 @@ -use std::ffi::{OsStr, OsString}; +use std::ffi::OsStr; +use std::num::ParseIntError; use std::path::Path; use std::process::Command; use std::thread; @@ -59,9 +60,6 @@ fn mtime(path: impl AsRef) -> SystemTime { } #[allow(clippy::missing_errors_doc)] -pub fn validate_port(arg: &OsStr) -> Result<(), OsString> { - match arg.to_string_lossy().parse::() { - Ok(_port) => Ok(()), - Err(err) => Err(OsString::from(err.to_string())), - } +pub fn validate_port(arg: &OsStr) -> Result<(), ParseIntError> { + arg.to_string_lossy().parse::().map(|_| ()) } diff --git a/clippy_lints/src/almost_complete_letter_range.rs b/clippy_lints/src/almost_complete_letter_range.rs new file mode 100644 index 00000000000..b364a370efa --- /dev/null +++ b/clippy_lints/src/almost_complete_letter_range.rs @@ -0,0 +1,100 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{trim_span, walk_span_to_context}; +use clippy_utils::{meets_msrv, msrvs}; +use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks for ranges which almost include the entire range of letters from 'a' to 'z', but + /// don't because they're a half open range. + /// + /// ### Why is this bad? + /// This (`'a'..'z'`) is almost certainly a typo meant to include all letters. + /// + /// ### Example + /// ```rust + /// let _ = 'a'..'z'; + /// ``` + /// Use instead: + /// ```rust + /// let _ = 'a'..='z'; + /// ``` + #[clippy::version = "1.63.0"] + pub ALMOST_COMPLETE_LETTER_RANGE, + suspicious, + "almost complete letter range" +} +impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]); + +pub struct AlmostCompleteLetterRange { + msrv: Option, +} +impl AlmostCompleteLetterRange { + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} +impl EarlyLintPass for AlmostCompleteLetterRange { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { + if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind { + let ctxt = e.span.ctxt(); + let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt) + && let Some(end) = walk_span_to_context(end.span, ctxt) + && meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) + { + Some((trim_span(cx.sess().source_map(), start.between(end)), "..=")) + } else { + None + }; + check_range(cx, e.span, start, end, sugg); + } + } + + fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) { + if let PatKind::Range(Some(start), Some(end), kind) = &p.kind + && matches!(kind.node, RangeEnd::Excluded) + { + let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) { + "..=" + } else { + "..." + }; + check_range(cx, p.span, start, end, Some((kind.span, sugg))); + } + } + + extract_msrv_attr!(EarlyContext); +} + +fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) { + if let ExprKind::Lit(start_lit) = &start.peel_parens().kind + && let ExprKind::Lit(end_lit) = &end.peel_parens().kind + && matches!( + (&start_lit.kind, &end_lit.kind), + (LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z')) + | (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z')) + ) + { + span_lint_and_then( + cx, + ALMOST_COMPLETE_LETTER_RANGE, + span, + "almost complete ascii letter range", + |diag| { + if let Some((span, sugg)) = sugg { + diag.span_suggestion( + span, + "use an inclusive range", + sugg.to_owned(), + Applicability::MaybeIncorrect, + ); + } + } + ); + } +} diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index da1b646f477..159f3b0cd01 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -28,7 +28,7 @@ declare_clippy_lint! { /// let x = 3.14; /// let y = 1_f64 / x; /// ``` - /// Use predefined constants instead: + /// Use instead: /// ```rust /// let x = std::f32::consts::PI; /// let y = std::f64::consts::FRAC_1_PI; diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs index 88b91d58907..6e5c8f44581 100644 --- a/clippy_lints/src/as_conversions.rs +++ b/clippy_lints/src/as_conversions.rs @@ -29,15 +29,14 @@ declare_clippy_lint! { /// f(a as u16); /// ``` /// - /// Usually better represents the semantics you expect: + /// Use instead: /// ```rust,ignore /// f(a.try_into()?); - /// ``` - /// or - /// ```rust,ignore + /// + /// // or + /// /// f(a.try_into().expect("Unexpected u16 overflow in f")); /// ``` - /// #[clippy::version = "1.41.0"] pub AS_CONVERSIONS, restriction, diff --git a/clippy_lints/src/as_underscore.rs b/clippy_lints/src/as_underscore.rs new file mode 100644 index 00000000000..464be4218dd --- /dev/null +++ b/clippy_lints/src/as_underscore.rs @@ -0,0 +1,74 @@ +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Check for the usage of `as _` conversion using inferred type. + /// + /// ### Why is this bad? + /// The conversion might include lossy conversion and dangerous cast that might go + /// undetected du to the type being inferred. + /// + /// The lint is allowed by default as using `_` is less wordy than always specifying the type. + /// + /// ### Example + /// ```rust + /// fn foo(n: usize) {} + /// let n: u16 = 256; + /// foo(n as _); + /// ``` + /// Use instead: + /// ```rust + /// fn foo(n: usize) {} + /// let n: u16 = 256; + /// foo(n as usize); + /// ``` + #[clippy::version = "1.63.0"] + pub AS_UNDERSCORE, + restriction, + "detects `as _` conversion" +} +declare_lint_pass!(AsUnderscore => [AS_UNDERSCORE]); + +impl<'tcx> LateLintPass<'tcx> for AsUnderscore { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + if let ExprKind::Cast(_, ty) = expr.kind && let TyKind::Infer = ty.kind { + + let ty_resolved = cx.typeck_results().expr_ty(expr); + if let ty::Error(_) = ty_resolved.kind() { + span_lint_and_help( + cx, + AS_UNDERSCORE, + expr.span, + "using `as _` conversion", + None, + "consider giving the type explicitly", + ); + } else { + span_lint_and_then( + cx, + AS_UNDERSCORE, + expr.span, + "using `as _` conversion", + |diag| { + diag.span_suggestion( + ty.span, + "consider giving the type explicitly", + format!("{}", ty_resolved), + Applicability::MachineApplicable, + ); + } + ); + } + } + } +} diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 4c2d3366483..f81da2d4223 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -27,10 +27,16 @@ declare_clippy_lint! { /// let mut a = 5; /// let b = 0; /// // ... - /// // Bad - /// a = a + b; /// - /// // Good + /// a = a + b; + /// ``` + /// + /// Use instead: + /// ```rust + /// let mut a = 5; + /// let b = 0; + /// // ... + /// /// a += b; /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 3de91f3d24a..770cb6a3d7b 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -89,13 +89,14 @@ declare_clippy_lint! { /// /// ### Example /// ```ignore - /// // Bad /// #[deny(dead_code)] /// extern crate foo; /// #[forbid(dead_code)] /// use foo::bar; + /// ``` /// - /// // Ok + /// Use instead: + /// ```rust,ignore /// #[allow(unused_imports)] /// use foo::baz; /// #[allow(unused_imports)] @@ -146,15 +147,19 @@ declare_clippy_lint! { /// /// ### Example /// ```rust + /// #[allow(dead_code)] + /// + /// fn not_quite_good_code() { } + /// ``` + /// + /// Use instead: + /// ```rust /// // Good (as inner attribute) /// #![allow(dead_code)] /// /// fn this_is_fine() { } /// - /// // Bad - /// #[allow(dead_code)] - /// - /// fn not_quite_good_code() { } + /// // or /// /// // Good (as outer attribute) /// #[allow(dead_code)] @@ -175,12 +180,11 @@ declare_clippy_lint! { /// These lints should only be enabled on a lint-by-lint basis and with careful consideration. /// /// ### Example - /// Bad: /// ```rust /// #![deny(clippy::restriction)] /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #![deny(clippy::as_conversions)] /// ``` @@ -205,13 +209,12 @@ declare_clippy_lint! { /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765) /// /// ### Example - /// Bad: /// ```rust /// #[cfg_attr(rustfmt, rustfmt_skip)] /// fn main() { } /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #[rustfmt::skip] /// fn main() { } @@ -231,20 +234,20 @@ declare_clippy_lint! { /// by the conditional compilation engine. /// /// ### Example - /// Bad: /// ```rust /// #[cfg(linux)] /// fn conditional() { } /// ``` /// - /// Good: + /// Use instead: /// ```rust + /// # mod hidden { /// #[cfg(target_os = "linux")] /// fn conditional() { } - /// ``` + /// # } + /// + /// // or /// - /// Or: - /// ```rust /// #[cfg(unix)] /// fn conditional() { } /// ``` @@ -266,14 +269,13 @@ declare_clippy_lint! { /// ensure that others understand the reasoning /// /// ### Example - /// Bad: /// ```rust /// #![feature(lint_reasons)] /// /// #![allow(clippy::some_lint)] /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #![feature(lint_reasons)] /// @@ -585,15 +587,21 @@ impl EarlyLintPass for EarlyAttributes { } fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { - for attr in &item.attrs { + let mut iter = item.attrs.iter().peekable(); + while let Some(attr) = iter.next() { if matches!(attr.kind, AttrKind::Normal(..)) && attr.style == AttrStyle::Outer && is_present_in_source(cx, attr.span) { let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent()); - let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt(), item.span.parent()); + let end_of_attr_to_next_attr_or_item = Span::new( + attr.span.hi(), + iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()), + item.span.ctxt(), + item.span.parent(), + ); - if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) { + if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) { let lines = snippet.split('\n').collect::>(); let lines = without_block_comments(lines); @@ -623,8 +631,15 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Opti if feature_item.has_name(sym::rustfmt); // check for `rustfmt_skip` and `rustfmt::skip` if let Some(skip_item) = &items[1].meta_item(); - if skip_item.has_name(sym!(rustfmt_skip)) || - skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip; + if skip_item.has_name(sym!(rustfmt_skip)) + || skip_item + .path + .segments + .last() + .expect("empty path in attribute") + .ident + .name + == sym::skip; // Only lint outer attributes, because custom inner attributes are unstable // Tracking issue: https://github.com/rust-lang/rust/issues/54726 if attr.style == AttrStyle::Outer; diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 4c4dd85d518..5bd7a342389 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -22,21 +22,17 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust - /// // Bad + /// # fn somefunc() -> bool { true }; /// if { true } { /* ... */ } /// - /// // Good - /// if true { /* ... */ } + /// if { let x = somefunc(); x } { /* ... */ } /// ``` /// - /// // or - /// + /// Use instead: /// ```rust /// # fn somefunc() -> bool { true }; - /// // Bad - /// if { let x = somefunc(); x } { /* ... */ } + /// if true { /* ... */ } /// - /// // Good /// let res = { let x = somefunc(); x }; /// if res { /* ... */ } /// ``` diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 0adb6327164..e4e122ba6eb 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -27,8 +27,14 @@ declare_clippy_lint! { /// /// ### Example /// ```ignore - /// if a && true // should be: if a - /// if !(a == b) // should be: if a != b + /// if a && true {} + /// if !(a == b) {} + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// if a {} + /// if a != b {} /// ``` #[clippy::version = "pre 1.29.0"] pub NONMINIMAL_BOOL, @@ -48,10 +54,15 @@ declare_clippy_lint! { /// Ignores short circuiting behavior. /// /// ### Example - /// ```ignore + /// ```rust,ignore + /// // The `b` is unnecessary, the expression is equivalent to `if a`. /// if a && b || a { ... } /// ``` - /// The `b` is unnecessary, the expression is equivalent to `if a`. + /// + /// Use instead: + /// ```rust,ignore + /// if a {} + /// ``` #[clippy::version = "pre 1.29.0"] pub LOGIC_BUG, correctness, diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs new file mode 100644 index 00000000000..ec2f31cf673 --- /dev/null +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -0,0 +1,118 @@ +use crate::reference::DEREF_ADDROF; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::implements_trait; +use clippy_utils::{get_parent_expr, is_lint_allowed}; +use rustc_errors::Applicability; +use rustc_hir::{ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::Mutability; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `&*(&T)`. + /// + /// ### Why is this bad? + /// Dereferencing and then borrowing a reference value has no effect in most cases. + /// + /// ### Known problems + /// false negative on such code: + /// ``` + /// let x = &12; + /// let addr_x = &x as *const _ as usize; + /// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggerd. + /// // But if we fix it, assert will fail. + /// assert_ne!(addr_x, addr_y); + /// ``` + /// + /// ### Example + /// ```rust + /// let s = &String::new(); + /// + /// // Bad + /// let a: &String = &* s; + /// foo(&*s); + /// + /// // Good + /// let a: &String = s; + /// foo(&**s); + /// + /// fn foo(_: &str){ } + /// ``` + #[clippy::version = "1.59.0"] + pub BORROW_DEREF_REF, + complexity, + "deref on an immutable reference returns the same type as itself" +} + +declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]); + +impl LateLintPass<'_> for BorrowDerefRef { + fn check_expr(&mut self, cx: &LateContext<'_>, e: &rustc_hir::Expr<'_>) { + if_chain! { + if !e.span.from_expansion(); + if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind; + if !addrof_target.span.from_expansion(); + if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind; + if !deref_target.span.from_expansion(); + if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) ); + let ref_ty = cx.typeck_results().expr_ty(deref_target); + if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind(); + then{ + + if let Some(parent_expr) = get_parent_expr(cx, e){ + if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) && + !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) { + return; + } + + // modification to `&mut &*x` is different from `&mut x` + if matches!(deref_target.kind, ExprKind::Path(..) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::Unary(UnOp::Deref, ..)) + && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) { + return; + } + } + + span_lint_and_then( + cx, + BORROW_DEREF_REF, + e.span, + "deref on an immutable reference", + |diag| { + diag.span_suggestion( + e.span, + "if you would like to reborrow, try removing `&*`", + snippet_opt(cx, deref_target.span).unwrap(), + Applicability::MachineApplicable + ); + + // has deref trait -> give 2 help + // doesn't have deref trait -> give 1 help + if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){ + if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) { + return; + } + } + + diag.span_suggestion( + e.span, + "if you would like to deref, try using `&**`", + format!( + "&**{}", + &snippet_opt(cx, deref_target.span).unwrap(), + ), + Applicability::MaybeIncorrect + ); + + } + ); + + } + } + } +} diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 02d97bf43df..bfdbaf2413a 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -28,7 +28,13 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let vec = vec![1_u8]; - /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead + /// let count = vec.iter().filter(|x| **x == 0u8).count(); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// # let vec = vec![1_u8]; + /// let count = bytecount::count(&vec, 0u8); /// ``` #[clippy::version = "pre 1.29.0"] pub NAIVE_BYTECOUNT, diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 6bac6bf83f8..64ea326b75a 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::{meets_msrv, msrvs}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_semver::RustcVersion; use super::CAST_ABS_TO_UNSIGNED; @@ -18,25 +17,28 @@ pub(super) fn check( cast_to: Ty<'_>, msrv: Option, ) { - if_chain! { - if meets_msrv(msrv, msrvs::UNSIGNED_ABS); - if cast_from.is_integral(); - if cast_to.is_integral(); - if cast_from.is_signed(); - if !cast_to.is_signed(); - if let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind; - if let method_name = method_path.ident.name.as_str(); - if method_name == "abs"; - then { - span_lint_and_sugg( - cx, - CAST_ABS_TO_UNSIGNED, - expr.span, - &format!("casting the result of `{}::{}()` to {}", cast_from, method_name, cast_to), - "replace with", - format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")), - Applicability::MachineApplicable, - ); - } + if meets_msrv(msrv, msrvs::UNSIGNED_ABS) + && let ty::Int(from) = cast_from.kind() + && let ty::Uint(to) = cast_to.kind() + && let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind + && method_path.ident.name.as_str() == "abs" + { + let span = if from.bit_width() == to.bit_width() { + expr.span + } else { + // if the result of `.unsigned_abs` would be a different type, keep the cast + // e.g. `i64 -> usize`, `i16 -> u8` + cast_expr.span + }; + + span_lint_and_sugg( + cx, + CAST_ABS_TO_UNSIGNED, + span, + &format!("casting the result of `{cast_from}::abs()` to {cast_to}"), + "replace with", + format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 7eeaaa01921..1010340c712 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -318,7 +318,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: if let QPath::TypeRelative(ty, path) = &path; if path.ident.name.as_str() == function; if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind; - if let [int] = &*tp.segments; + if let [int] = tp.segments; then { let name = int.ident.name.as_str(); candidates.iter().find(|c| &name == *c).copied() @@ -332,7 +332,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { if_chain! { if let QPath::Resolved(_, path) = *path; - if let [ty] = &*path.segments; + if let [ty] = path.segments; then { let name = ty.ident.name.as_str(); INTS.iter().find(|c| &name == *c).copied() diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 317c4bfb322..33c44f8b2db 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// complexity. /// /// ### Example - /// No. You'll see it when you get the warning. + /// You'll see it when you get the warning. #[clippy::version = "1.35.0"] pub COGNITIVE_COMPLEXITY, nursery, diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 3227e6e86af..3eceb848822 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -41,7 +41,7 @@ declare_clippy_lint! { /// /// ``` /// - /// Should be written: + /// Use instead: /// /// ```rust,ignore /// if x && y { diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 399d11472b0..913e081af3b 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -34,7 +34,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// Could be written: + /// Use instead: /// /// ```rust,ignore /// use std::cmp::Ordering; diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index e6a0162fd02..1e9a1153011 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -141,7 +141,7 @@ declare_clippy_lint! { /// }; /// ``` /// - /// Could be written as: + /// Use instead: /// ```ignore /// println!("Hello World"); /// let foo = if … { diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index fc141b4a6e3..9b8a481b6ea 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -90,7 +90,7 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { while let Some(curr) = cursor.next() { if_chain! { if !prev_is_dollar; - if let Some(span) = is_crate_keyword(&curr); + if let Some(span) = is_crate_keyword(curr); if let Some(next) = cursor.look_ahead(0); if is_token(next, &TokenKind::ModSep); then { @@ -103,7 +103,7 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { return span; } } - prev_is_dollar = is_token(&curr, &TokenKind::Dollar); + prev_is_dollar = is_token(curr, &TokenKind::Dollar); } None } diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index f99d793c201..17deccf8c39 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; declare_clippy_lint! { @@ -30,14 +30,27 @@ declare_clippy_lint! { "`dbg!` macro is intended as a debugging tool" } -declare_lint_pass!(DbgMacro => [DBG_MACRO]); +#[derive(Copy, Clone)] +pub struct DbgMacro { + allow_dbg_in_tests: bool, +} + +impl_lint_pass!(DbgMacro => [DBG_MACRO]); + +impl DbgMacro { + pub fn new(allow_dbg_in_tests: bool) -> Self { + DbgMacro { allow_dbg_in_tests } + } +} impl LateLintPass<'_> for DbgMacro { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) { - // we make an exception for test code - if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) { + // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml + if self.allow_dbg_in_tests + && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) + { return; } let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 34a5f8444de..e98691fd5bb 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -21,7 +21,7 @@ declare_clippy_lint! { /// bar: bool /// } /// - /// impl std::default::Default for Foo { + /// impl Default for Foo { /// fn default() -> Self { /// Self { /// bar: false diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index fe99f4a8d55..99347ebadc6 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,16 +1,17 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::paths; -use clippy_utils::ty::{implements_trait, is_copy}; +use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{is_lint_allowed, match_def_path}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; use rustc_hir::{ - BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety, + self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource, Unsafety, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, BoundConstness, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; @@ -224,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { fn check_hash_peq<'tcx>( cx: &LateContext<'tcx>, span: Span, - trait_ref: &TraitRef<'_>, + trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, hash_is_automatically_derived: bool, ) { @@ -277,7 +278,7 @@ fn check_hash_peq<'tcx>( fn check_ord_partial_ord<'tcx>( cx: &LateContext<'tcx>, span: Span, - trait_ref: &TraitRef<'_>, + trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, ord_is_automatically_derived: bool, ) { @@ -328,7 +329,7 @@ fn check_ord_partial_ord<'tcx>( } /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. -fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { +fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { let clone_id = match cx.tcx.lang_items().clone_trait() { Some(id) if trait_ref.trait_def_id() == Some(id) => id, _ => return, @@ -378,7 +379,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &T fn check_unsafe_derive_deserialize<'tcx>( cx: &LateContext<'tcx>, item: &Item<'_>, - trait_ref: &TraitRef<'_>, + trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, ) { fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { @@ -455,13 +456,41 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { } /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. -fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { +fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { if_chain! { if let ty::Adt(adt, substs) = ty.kind(); if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); + if let Some(peq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::PartialEq); if let Some(def_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); - if !implements_trait(cx, ty, eq_trait_def_id, substs); + // New `ParamEnv` replacing `T: PartialEq` with `T: Eq` + let param_env = ParamEnv::new( + cx.tcx.mk_predicates(cx.param_env.caller_bounds().iter().map(|p| { + let kind = p.kind(); + match kind.skip_binder() { + PredicateKind::Trait(p) + if p.trait_ref.def_id == peq_trait_def_id + && p.trait_ref.substs.get(0) == p.trait_ref.substs.get(1) + && matches!(p.trait_ref.self_ty().kind(), ty::Param(_)) + && p.constness == BoundConstness::NotConst + && p.polarity == ImplPolarity::Positive => + { + cx.tcx.mk_predicate(kind.rebind(PredicateKind::Trait(TraitPredicate { + trait_ref: TraitRef::new( + eq_trait_def_id, + cx.tcx.mk_substs([GenericArg::from(p.trait_ref.self_ty())].into_iter()), + ), + constness: BoundConstness::NotConst, + polarity: ImplPolarity::Positive, + }))) + }, + _ => p, + } + })), + cx.param_env.reveal(), + cx.param_env.constness(), + ); + if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, substs); then { // If all of our fields implement `Eq`, we can implement `Eq` too for variant in adt.variants() { diff --git a/clippy_lints/src/doc_link_with_quotes.rs b/clippy_lints/src/doc_link_with_quotes.rs new file mode 100644 index 00000000000..cb07f57e870 --- /dev/null +++ b/clippy_lints/src/doc_link_with_quotes.rs @@ -0,0 +1,60 @@ +use clippy_utils::diagnostics::span_lint; +use itertools::Itertools; +use rustc_ast::{AttrKind, Attribute}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks) + /// outside of code blocks + /// ### Why is this bad? + /// It is likely a typo when defining an intra-doc link + /// + /// ### Example + /// ```rust + /// /// See also: ['foo'] + /// fn bar() {} + /// ``` + /// Use instead: + /// ```rust + /// /// See also: [`foo`] + /// fn bar() {} + /// ``` + #[clippy::version = "1.60.0"] + pub DOC_LINK_WITH_QUOTES, + pedantic, + "possible typo for an intra-doc link" +} +declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]); + +impl EarlyLintPass for DocLinkWithQuotes { + fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) { + if let AttrKind::DocComment(_, symbol) = attr.kind { + if contains_quote_link(symbol.as_str()) { + span_lint( + ctx, + DOC_LINK_WITH_QUOTES, + attr.span, + "possible intra-doc link using quotes instead of backticks", + ); + } + } + } +} + +fn contains_quote_link(s: &str) -> bool { + let mut in_backticks = false; + let mut found_opening = false; + + for c in s.chars().tuple_windows::<(char, char)>() { + match c { + ('`', _) => in_backticks = !in_backticks, + ('[', '\'') if !in_backticks => found_opening = true, + ('\'', ']') if !in_backticks && found_opening => return true, + _ => {}, + } + } + + false +} diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index be95375789d..ee0440e52ff 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -24,7 +24,7 @@ declare_clippy_lint! { /// if x == y || x < y {} /// ``` /// - /// Could be written as: + /// Use instead: /// /// ```rust /// # let x = 1; diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index e10f740d24a..a33ef5ce6e3 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -13,23 +13,21 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// fn simple_double_parens() -> i32 { /// ((0)) /// } /// - /// // Good + /// # fn foo(bar: usize) {} + /// foo((0)); + /// ``` + /// + /// Use instead: + /// ```rust /// fn simple_no_parens() -> i32 { /// 0 /// } /// - /// // or - /// /// # fn foo(bar: usize) {} - /// // Bad - /// foo((0)); - /// - /// // Good /// foo(0); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 09318f74527..d85ace3a279 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -22,15 +22,17 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # use std::time::Duration; - /// let dur = Duration::new(5, 0); + /// # let duration = Duration::new(5, 0); + /// let micros = duration.subsec_nanos() / 1_000; + /// let millis = duration.subsec_nanos() / 1_000_000; + /// ``` /// - /// // Bad - /// let _micros = dur.subsec_nanos() / 1_000; - /// let _millis = dur.subsec_nanos() / 1_000_000; - /// - /// // Good - /// let _micros = dur.subsec_micros(); - /// let _millis = dur.subsec_millis(); + /// Use instead: + /// ```rust + /// # use std::time::Duration; + /// # let duration = Duration::new(5, 0); + /// let micros = duration.subsec_micros(); + /// let millis = duration.subsec_millis(); /// ``` #[clippy::version = "pre 1.29.0"] pub DURATION_SUBSEC, diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs index 0b9f54231c5..bf4488570ea 100644 --- a/clippy_lints/src/else_if_without_else.rs +++ b/clippy_lints/src/else_if_without_else.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// Could be written: + /// Use instead: /// /// ```rust /// # fn a() {} diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index b5d6b3c7524..bbebc024414 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -23,12 +23,11 @@ declare_clippy_lint! { /// /// /// ### Example - /// Bad: /// ```rust /// enum Test {} /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #![feature(never_type)] /// diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index c5a987842c3..27743a0ebec 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -46,7 +46,7 @@ declare_clippy_lint! { /// map.insert(k, v); /// } /// ``` - /// can both be rewritten as: + /// Use instead: /// ```rust /// # use std::collections::HashMap; /// # let mut map = HashMap::new(); diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index e029b8e8537..263a5b573c9 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -32,7 +32,7 @@ declare_clippy_lint! { /// BattenbergCake, /// } /// ``` - /// Could be written as: + /// Use instead: /// ```rust /// enum Cake { /// BlackForest, diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index afb5d32f953..c3176d987c6 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -30,9 +30,9 @@ declare_clippy_lint! { /// ```rust /// # let x = 1; /// if x + 1 == x + 1 {} - /// ``` - /// or - /// ```rust + /// + /// // or + /// /// # let a = 3; /// # let b = 4; /// assert_eq!(a, a); diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index cf47e581ccb..ef1216358dd 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// do_thing(); /// } /// ``` - /// Should be written + /// Use instead: /// ```rust,ignore /// if x == Some(2) { /// do_thing(); diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 807ecd2ddd1..9d21dd71e0e 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -31,12 +31,14 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # fn foo(bar: usize) {} - /// // Bad /// let x = Box::new(1); /// foo(*x); /// println!("{}", *x); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # fn foo(bar: usize) {} /// let x = 1; /// foo(x); /// println!("{}", x); diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 7a81fb37e84..a2af10e2ba5 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -18,7 +18,6 @@ declare_clippy_lint! { /// readability and API. /// /// ### Example - /// Bad: /// ```rust /// struct S { /// is_pending: bool, @@ -27,7 +26,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// Good: + /// Use instead: /// ```rust /// enum S { /// Pending, diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index d8f765b288a..12d636cf410 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -22,8 +22,16 @@ declare_clippy_lint! { /// ```rust /// # use std::io::Write; /// # let bar = "furchtbar"; - /// // this would be clearer as `eprintln!("foo: {:?}", bar);` /// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap(); + /// writeln!(&mut std::io::stdout(), "foo: {:?}", bar).unwrap(); + /// ``` + /// + /// Use instead: + /// ```rust + /// # use std::io::Write; + /// # let bar = "furchtbar"; + /// eprintln!("foo: {:?}", bar); + /// println!("foo: {:?}", bar); /// ``` #[clippy::version = "pre 1.29.0"] pub EXPLICIT_WRITE, diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 9f868df3ad0..b88e53aeca6 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -20,7 +20,6 @@ declare_clippy_lint! { /// ```rust /// struct Foo(i32); /// - /// // Bad /// impl From for Foo { /// fn from(s: String) -> Self { /// Foo(s.parse().unwrap()) @@ -28,8 +27,8 @@ declare_clippy_lint! { /// } /// ``` /// + /// Use instead: /// ```rust - /// // Good /// struct Foo(i32); /// /// impl TryFrom for Foo { diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 7a4397a7b74..f850ea31f4d 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -19,11 +19,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let v: f32 = 0.123_456_789_9; /// println!("{}", v); // 0.123_456_789 + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let v: f64 = 0.123_456_789_9; /// println!("{}", v); // 0.123_456_789_9 /// ``` diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 42503c26de1..df9b41d2c98 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -35,8 +35,7 @@ declare_clippy_lint! { /// let _ = a.exp() - 1.0; /// ``` /// - /// is better expressed as - /// + /// Use instead: /// ```rust /// let a = 3f32; /// let _ = a.cbrt(); diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 64c41b56587..3084c70589f 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -25,12 +25,13 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust - /// - /// // Bad /// let foo = "foo"; /// format!("{}", foo); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// let foo = "foo"; /// foo.to_owned(); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 57964b8d48e..db0166da57f 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -36,12 +36,18 @@ declare_clippy_lint! { /// This is either a typo in the binary operator or confusing. /// /// ### Example - /// ```rust,ignore - /// if foo <- 30 { // this should be `foo < -30` but looks like a different operator - /// } + /// ```rust + /// # let foo = true; + /// # let bar = false; + /// // &&! looks like a different operator + /// if foo &&! bar {} + /// ``` /// - /// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator - /// } + /// Use instead: + /// ```rust + /// # let foo = true; + /// # let bar = false; + /// if foo && !bar {} /// ``` #[clippy::version = "1.40.0"] pub SUSPICIOUS_UNARY_OP_FORMATTING, diff --git a/clippy_lints/src/get_first.rs b/clippy_lints/src/get_first.rs new file mode 100644 index 00000000000..0748ab45252 --- /dev/null +++ b/clippy_lints/src/get_first.rs @@ -0,0 +1,69 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{is_slice_of_primitives, match_def_path, paths}; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +declare_clippy_lint! { + /// ### What it does + /// Checks for using `x.get(0)` instead of + /// `x.first()`. + /// + /// ### Why is this bad? + /// Using `x.first()` is easier to read and has the same + /// result. + /// + /// ### Example + /// ```rust + /// // Bad + /// let x = vec![2, 3, 5]; + /// let first_element = x.get(0); + /// ``` + /// Use instead: + /// ```rust + /// // Good + /// let x = vec![2, 3, 5]; + /// let first_element = x.first(); + /// ``` + #[clippy::version = "1.63.0"] + pub GET_FIRST, + style, + "Using `x.get(0)` when `x.first()` is simpler" +} +declare_lint_pass!(GetFirst => [GET_FIRST]); + +impl<'tcx> LateLintPass<'tcx> for GetFirst { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &expr.kind; + if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, expr_def_id, &paths::SLICE_GET); + + if let Some(_) = is_slice_of_primitives(cx, struct_calling_on); + if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind; + + then { + let mut applicability = Applicability::MachineApplicable; + let slice_name = snippet_with_applicability( + cx, + struct_calling_on.span, "..", + &mut applicability, + ); + span_lint_and_sugg( + cx, + GET_FIRST, + expr.span, + &format!("accessing first element with `{0}.get(0)`", slice_name), + "try", + format!("{}.first()", slice_name), + applicability, + ); + } + } + } +} diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs deleted file mode 100644 index df29d9308e7..00000000000 --- a/clippy_lints/src/get_last_with_len.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! lint on using `x.get(x.len() - 1)` instead of `x.last()` - -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::SpanlessEq; -use if_chain::if_chain; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for using `x.get(x.len() - 1)` instead of - /// `x.last()`. - /// - /// ### Why is this bad? - /// Using `x.last()` is easier to read and has the same - /// result. - /// - /// Note that using `x[x.len() - 1]` is semantically different from - /// `x.last()`. Indexing into the array will panic on out-of-bounds - /// accesses, while `x.get()` and `x.last()` will return `None`. - /// - /// There is another lint (get_unwrap) that covers the case of using - /// `x.get(index).unwrap()` instead of `x[index]`. - /// - /// ### Example - /// ```rust - /// // Bad - /// let x = vec![2, 3, 5]; - /// let last_element = x.get(x.len() - 1); - /// - /// // Good - /// let x = vec![2, 3, 5]; - /// let last_element = x.last(); - /// ``` - #[clippy::version = "1.37.0"] - pub GET_LAST_WITH_LEN, - complexity, - "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler" -} - -declare_lint_pass!(GetLastWithLen => [GET_LAST_WITH_LEN]); - -impl<'tcx> LateLintPass<'tcx> for GetLastWithLen { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - // Is a method call - if let ExprKind::MethodCall(path, args, _) = expr.kind; - - // Method name is "get" - if path.ident.name == sym!(get); - - // Argument 0 (the struct we're calling the method on) is a vector - if let Some(struct_calling_on) = args.get(0); - let struct_ty = cx.typeck_results().expr_ty(struct_calling_on); - if is_type_diagnostic_item(cx, struct_ty, sym::Vec); - - // Argument to "get" is a subtraction - if let Some(get_index_arg) = args.get(1); - if let ExprKind::Binary( - Spanned { - node: BinOpKind::Sub, - .. - }, - lhs, - rhs, - ) = &get_index_arg.kind; - - // LHS of subtraction is "x.len()" - if let ExprKind::MethodCall(arg_lhs_path, lhs_args, _) = &lhs.kind; - if arg_lhs_path.ident.name == sym::len; - if let Some(arg_lhs_struct) = lhs_args.get(0); - - // The two vectors referenced (x in x.get(...) and in x.len()) - if SpanlessEq::new(cx).eq_expr(struct_calling_on, arg_lhs_struct); - - // RHS of subtraction is 1 - if let ExprKind::Lit(rhs_lit) = &rhs.kind; - if let LitKind::Int(1, ..) = rhs_lit.node; - - then { - let mut applicability = Applicability::MachineApplicable; - let vec_name = snippet_with_applicability( - cx, - struct_calling_on.span, "vec", - &mut applicability, - ); - - span_lint_and_sugg( - cx, - GET_LAST_WITH_LEN, - expr.span, - &format!("accessing last element with `{0}.get({0}.len() - 1)`", vec_name), - "try", - format!("{}.last()", vec_name), - applicability, - ); - } - } - } -} diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 40cc5cd4bcf..419ea5a6811 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,15 +1,14 @@ -use clippy_utils::get_parent_expr; -use clippy_utils::source::snippet; -use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; +use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{clip, unsext}; +use rustc_errors::Applicability; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; -use clippy_utils::diagnostics::span_lint; -use clippy_utils::{clip, unsext}; - declare_clippy_lint! { /// ### What it does /// Checks for identity operations, e.g., `x + 0`. @@ -23,11 +22,6 @@ declare_clippy_lint! { /// # let x = 1; /// x / 1 + 0 * 1 - 0 | 0; /// ``` - /// - /// ### Known problems - /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to - /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code. - /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724) #[clippy::version = "pre 1.29.0"] pub IDENTITY_OP, complexity, @@ -45,31 +39,22 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { if !is_allowed(cx, *cmp, left, right) { match cmp.node { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - if reducible_to_right(cx, expr, right) { - check(cx, left, 0, expr.span, right.span); - } - check(cx, right, 0, expr.span, left.span); + check(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right)); + check(cx, right, 0, expr.span, left.span, Parens::Unneeded); }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - check(cx, right, 0, expr.span, left.span); + check(cx, right, 0, expr.span, left.span, Parens::Unneeded); }, BinOpKind::Mul => { - if reducible_to_right(cx, expr, right) { - check(cx, left, 1, expr.span, right.span); - } - check(cx, right, 1, expr.span, left.span); + check(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right)); + check(cx, right, 1, expr.span, left.span, Parens::Unneeded); }, - BinOpKind::Div => check(cx, right, 1, expr.span, left.span), + BinOpKind::Div => check(cx, right, 1, expr.span, left.span, Parens::Unneeded), BinOpKind::BitAnd => { - if reducible_to_right(cx, expr, right) { - check(cx, left, -1, expr.span, right.span); - } - check(cx, right, -1, expr.span, left.span); - }, - BinOpKind::Rem => { - // Don't call reducible_to_right because N % N is always reducible to 1 - check_remainder(cx, left, right, expr.span, left.span); + check(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right)); + check(cx, right, -1, expr.span, left.span, Parens::Unneeded); }, + BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span), _ => (), } } @@ -77,24 +62,50 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { } } -/// Checks if `left op ..right` can be actually reduced to `right` -/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` -/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` -/// See #8724 -fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool { - if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind { - is_toplevel_binary(cx, binary) - } else { - true - } +#[derive(Copy, Clone)] +enum Parens { + Needed, + Unneeded, } -fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool { - if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind { - false - } else { - true +/// Checks if `left op right` needs parenthesis when reduced to `right` +/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced +/// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be +/// interpreted as a statement +/// +/// See #8724 +fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> Parens { + match right.kind { + ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => { + // ensure we're checking against the leftmost expression of `right` + // + // ~~~ `lhs` + // 0 + {4} * 2 + // ~~~~~~~ `right` + return needs_parenthesis(cx, binary, lhs); + }, + ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {}, + _ => return Parens::Unneeded, } + + let mut prev_id = binary.hir_id; + for (_, node) in cx.tcx.hir().parent_iter(binary.hir_id) { + if let Node::Expr(expr) = node + && let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) = expr.kind + && lhs.hir_id == prev_id + { + // keep going until we find a node that encompasses left of `binary` + prev_id = expr.hir_id; + continue; + } + + match node { + Node::Block(_) | Node::Stmt(_) => break, + _ => return Parens::Unneeded, + }; + } + + Parens::Needed } fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { @@ -115,11 +126,11 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, } { - span_ineffective_operation(cx, span, arg); + span_ineffective_operation(cx, span, arg, Parens::Unneeded); } } -fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { +fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), @@ -132,19 +143,27 @@ fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { 1 => v == 1, _ => unreachable!(), } { - span_ineffective_operation(cx, span, arg); + span_ineffective_operation(cx, span, arg, parens); } } } -fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) { - span_lint( +fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span, parens: Parens) { + let mut applicability = Applicability::MachineApplicable; + let expr_snippet = snippet_with_applicability(cx, arg, "..", &mut applicability); + + let suggestion = match parens { + Parens::Needed => format!("({expr_snippet})"), + Parens::Unneeded => expr_snippet.into_owned(), + }; + + span_lint_and_sugg( cx, IDENTITY_OP, span, - &format!( - "the operation is ineffective. Consider reducing it to `{}`", - snippet(cx, arg, "..") - ), + "this operation has no effect", + "consider reducing it to", + suggestion, + applicability, ); } diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 0f3889a2936..63ac092dfaf 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,12 +1,13 @@ //! lint when there is a large size difference between variants on an enum -use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{diagnostics::span_lint_and_then, ty::is_copy}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{Adt, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -26,6 +27,15 @@ declare_clippy_lint! { /// the overhead is negligible and the boxing is counter-productive. Always /// measure the change this lint suggests. /// + /// For types that implement `Copy`, the suggestion to `Box` a variant's + /// data would require removing the trait impl. The types can of course + /// still be `Clone`, but that is worse ergonomically. Depending on the + /// use case it may be possible to store the large data in an auxillary + /// structure (e.g. Arena or ECS). + /// + /// The lint will ignore generic types if the layout depends on the + /// generics, even if the size difference will be large anyway. + /// /// ### Example /// ```rust /// // Bad @@ -74,7 +84,7 @@ struct VariantInfo { impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { if in_external_macro(cx.tcx.sess, item.span) { return; } @@ -132,37 +142,43 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { let fields = def.variants[variants_size[0].ind].data.fields(); variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size))); let mut applicability = Applicability::MaybeIncorrect; - let sugg: Vec<(Span, String)> = variants_size[0] - .fields_size - .iter() - .rev() - .map_while(|val| { - if difference > self.maximum_size_difference_allowed { - difference = difference.saturating_sub(val.size); - Some(( - fields[val.ind].ty.span, - format!( - "Box<{}>", - snippet_with_applicability( - cx, - fields[val.ind].ty.span, - "..", - &mut applicability - ) - .into_owned() - ), - )) - } else { - None - } - }) - .collect(); + if is_copy(cx, ty) || maybe_copy(cx, ty) { + diag.span_note( + item.ident.span, + "boxing a variant would require the type no longer be `Copy`", + ); + } else { + let sugg: Vec<(Span, String)> = variants_size[0] + .fields_size + .iter() + .rev() + .map_while(|val| { + if difference > self.maximum_size_difference_allowed { + difference = difference.saturating_sub(val.size); + Some(( + fields[val.ind].ty.span, + format!( + "Box<{}>", + snippet_with_applicability( + cx, + fields[val.ind].ty.span, + "..", + &mut applicability + ) + .into_owned() + ), + )) + } else { + None + } + }) + .collect(); - if !sugg.is_empty() { - diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect); - return; + if !sugg.is_empty() { + diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect); + return; + } } - diag.span_help(def.variants[variants_size[0].ind].span, help_text); }, ); @@ -170,3 +186,13 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { } } } + +fn maybe_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let Adt(_def, substs) = ty.kind() + && substs.types().next().is_some() + && let Some(copy_trait) = cx.tcx.lang_items().copy_trait() + { + return cx.tcx.non_blanket_impls_for_ty(copy_trait, ty).next().is_some(); + } + false +} diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index db09d00d730..56bbbbbc819 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind; if !is_local_used(cx, *cond, canonical_id); if let hir::ExprKind::Block(then, _) = then.kind; - if let Some(value) = check_assign(cx, canonical_id, &*then); + if let Some(value) = check_assign(cx, canonical_id, then); if !is_local_used(cx, value, canonical_id); then { let span = stmt.span.to(if_.span); diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index be5c478900f..d4ec046d0bb 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -4,6 +4,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), + LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), LintId::of(approx_const::APPROX_CONSTANT), LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(assign_ops::ASSIGN_OP_PATTERN), @@ -24,6 +25,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(borrow_deref_ref::BORROW_DEREF_REF), LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), @@ -36,7 +38,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(casts::UNNECESSARY_CAST), LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(collapsible_match::COLLAPSIBLE_MATCH), LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), @@ -91,7 +92,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(functions::RESULT_UNIT_ERR), LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(get_last_with_len::GET_LAST_WITH_LEN), + LintId::of(get_first::GET_FIRST), LintId::of(identity_op::IDENTITY_OP), LintId::of(if_let_mutex::IF_LET_MUTEX), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), @@ -132,23 +133,25 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_bits::MANUAL_BITS), - LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(map_clone::MAP_CLONE), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(match_result_ok::MATCH_RESULT_OK), - LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH), + LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MANUAL_MAP), + LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(matches::MATCH_OVERLAPPING_ARM), LintId::of(matches::MATCH_REF_PATS), LintId::of(matches::MATCH_SINGLE_BINDING), + LintId::of(matches::MATCH_STR_CASE_MISMATCH), LintId::of(matches::NEEDLESS_MATCH), LintId::of(matches::REDUNDANT_PATTERN_MATCHING), + LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(matches::SINGLE_MATCH), LintId::of(matches::WILDCARD_IN_OR_PATTERNS), LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), @@ -166,6 +169,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_NEXT), LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::GET_LAST_WITH_LEN), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::INTO_ITER_ON_REF), LintId::of(methods::IS_DIGIT_ASCII_RADIX), @@ -189,6 +193,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEW_RET_NO_SELF), + LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::OK_EXPECT), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), @@ -278,7 +283,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(self_assignment::SELF_ASSIGNMENT), LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(serde_api::SERDE_API_MISUSE), - LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -289,6 +293,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(swap::ALMOST_SWAPPED), LintId::of(swap::MANUAL_SWAP), + LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), @@ -302,6 +307,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), + LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmuting_null::TRANSMUTING_NULL), LintId::of(types::BORROWED_BOX), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index b15c979d0c7..4f1c3673f85 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -5,6 +5,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(borrow_deref_ref::BORROW_DEREF_REF), LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), @@ -15,7 +16,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(format::USELESS_FORMAT), LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(get_last_with_len::GET_LAST_WITH_LEN), LintId::of(identity_op::IDENTITY_OP), LintId::of(int_plus_one::INT_PLUS_ONE), LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), @@ -25,9 +25,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(loops::SINGLE_ELEMENT_LOOP), LintId::of(loops::WHILE_LET_LOOP), LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_SINGLE_BINDING), LintId::of(matches::NEEDLESS_MATCH), @@ -37,6 +37,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_NEXT), LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::GET_LAST_WITH_LEN), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::ITER_COUNT), LintId::of(methods::MANUAL_FILTER_MAP), @@ -90,6 +91,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), + LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(types::BORROWED_BOX), LintId::of(types::TYPE_COMPLEXITY), LintId::of(types::VEC_BOX), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index 6bf2c4bbaed..50cdd0af923 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -39,7 +39,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::NEVER_LOOP), LintId::of(loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH), + LintId::of(matches::MATCH_STR_CASE_MISMATCH), LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::ITERATOR_STEP_BY_ZERO), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 5552ea8aa80..b927ba3b17c 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -34,10 +34,10 @@ store.register_lints(&[ #[cfg(feature = "internal")] utils::internal_lints::UNNECESSARY_SYMBOL_STR, absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, + almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, approx_const::APPROX_CONSTANT, - arithmetic::FLOAT_ARITHMETIC, - arithmetic::INTEGER_ARITHMETIC, as_conversions::AS_CONVERSIONS, + as_underscore::AS_UNDERSCORE, asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, assertions_on_constants::ASSERTIONS_ON_CONSTANTS, @@ -64,6 +64,7 @@ store.register_lints(&[ booleans::LOGIC_BUG, booleans::NONMINIMAL_BOOL, borrow_as_ptr::BORROW_AS_PTR, + borrow_deref_ref::BORROW_DEREF_REF, bytecount::NAIVE_BYTECOUNT, bytes_count_to_len::BYTES_COUNT_TO_LEN, cargo::CARGO_COMMON_METADATA, @@ -93,7 +94,6 @@ store.register_lints(&[ cognitive_complexity::COGNITIVE_COMPLEXITY, collapsible_if::COLLAPSIBLE_ELSE_IF, collapsible_if::COLLAPSIBLE_IF, - collapsible_match::COLLAPSIBLE_MATCH, comparison_chain::COMPARISON_CHAIN, copies::BRANCHES_SHARING_CODE, copies::IFS_SAME_COND, @@ -124,6 +124,7 @@ store.register_lints(&[ doc::MISSING_PANICS_DOC, doc::MISSING_SAFETY_DOC, doc::NEEDLESS_DOCTEST_MAIN, + doc_link_with_quotes::DOC_LINK_WITH_QUOTES, double_comparison::DOUBLE_COMPARISONS, double_parens::DOUBLE_PARENS, drop_forget_ref::DROP_COPY, @@ -183,7 +184,7 @@ store.register_lints(&[ functions::TOO_MANY_ARGUMENTS, functions::TOO_MANY_LINES, future_not_send::FUTURE_NOT_SEND, - get_last_with_len::GET_LAST_WITH_LEN, + get_first::GET_FIRST, identity_op::IDENTITY_OP, if_let_mutex::IF_LET_MUTEX, if_not_else::IF_NOT_ELSE, @@ -250,33 +251,36 @@ store.register_lints(&[ manual_assert::MANUAL_ASSERT, manual_async_fn::MANUAL_ASYNC_FN, manual_bits::MANUAL_BITS, - manual_map::MANUAL_MAP, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_ok_or::MANUAL_OK_OR, manual_strip::MANUAL_STRIP, - manual_unwrap_or::MANUAL_UNWRAP_OR, map_clone::MAP_CLONE, map_err_ignore::MAP_ERR_IGNORE, map_unit_fn::OPTION_MAP_UNIT_FN, map_unit_fn::RESULT_MAP_UNIT_FN, - match_on_vec_items::MATCH_ON_VEC_ITEMS, match_result_ok::MATCH_RESULT_OK, - match_str_case_mismatch::MATCH_STR_CASE_MISMATCH, + matches::COLLAPSIBLE_MATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, + matches::MANUAL_MAP, + matches::MANUAL_UNWRAP_OR, matches::MATCH_AS_REF, matches::MATCH_BOOL, matches::MATCH_LIKE_MATCHES_MACRO, + matches::MATCH_ON_VEC_ITEMS, matches::MATCH_OVERLAPPING_ARM, matches::MATCH_REF_PATS, matches::MATCH_SAME_ARMS, matches::MATCH_SINGLE_BINDING, + matches::MATCH_STR_CASE_MISMATCH, matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, matches::MATCH_WILD_ERR_ARM, matches::NEEDLESS_MATCH, matches::REDUNDANT_PATTERN_MATCHING, matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, + matches::SIGNIFICANT_DROP_IN_SCRUTINEE, matches::SINGLE_MATCH, matches::SINGLE_MATCH_ELSE, + matches::TRY_ERR, matches::WILDCARD_ENUM_MATCH_ARM, matches::WILDCARD_IN_OR_PATTERNS, mem_forget::MEM_FORGET, @@ -302,6 +306,7 @@ store.register_lints(&[ methods::FLAT_MAP_IDENTITY, methods::FLAT_MAP_OPTION, methods::FROM_ITER_INSTEAD_OF_COLLECT, + methods::GET_LAST_WITH_LEN, methods::GET_UNWRAP, methods::IMPLICIT_CLONE, methods::INEFFICIENT_TO_STRING, @@ -330,6 +335,7 @@ store.register_lints(&[ methods::NEEDLESS_OPTION_TAKE, methods::NEEDLESS_SPLITN, methods::NEW_RET_NO_SELF, + methods::NO_EFFECT_REPLACE, methods::OK_EXPECT, methods::OPTION_AS_REF_DEREF, methods::OPTION_FILTER_MAP, @@ -377,6 +383,7 @@ store.register_lints(&[ misc_early::UNNEEDED_WILDCARD_PATTERN, misc_early::UNSEPARATED_LITERAL_SUFFIX, misc_early::ZERO_PREFIXED_LITERAL, + mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER, missing_const_for_fn::MISSING_CONST_FOR_FN, missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, @@ -418,6 +425,8 @@ store.register_lints(&[ non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY, nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, + numeric_arithmetic::FLOAT_ARITHMETIC, + numeric_arithmetic::INTEGER_ARITHMETIC, octal_escapes::OCTAL_ESCAPES, only_used_in_recursion::ONLY_USED_IN_RECURSION, open_options::NONSENSICAL_OPEN_OPTIONS, @@ -473,7 +482,6 @@ store.register_lints(&[ shadow::SHADOW_REUSE, shadow::SHADOW_SAME, shadow::SHADOW_UNRELATED, - significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE, single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES, single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, @@ -493,6 +501,7 @@ store.register_lints(&[ suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, swap::ALMOST_SWAPPED, swap::MANUAL_SWAP, + swap_ptr_to_ref::SWAP_PTR_TO_REF, tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, temporary_assignment::TEMPORARY_ASSIGNMENT, to_digit_is_some::TO_DIGIT_IS_SOME, @@ -514,7 +523,6 @@ store.register_lints(&[ transmute::USELESS_TRANSMUTE, transmute::WRONG_TRANSMUTE, transmuting_null::TRANSMUTING_NULL, - try_err::TRY_ERR, types::BORROWED_BOX, types::BOX_COLLECTION, types::LINKEDLIST, @@ -544,6 +552,7 @@ store.register_lints(&[ unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, unused_async::UNUSED_ASYNC, unused_io_amount::UNUSED_IO_AMOUNT, + unused_rounding::UNUSED_ROUNDING, unused_self::UNUSED_SELF, unused_unit::UNUSED_UNIT, unwrap::PANICKING_UNWRAP, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 34d1555049d..642d629971d 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -25,11 +25,10 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(regex::TRIVIAL_REGEX), - LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), - LintId::of(transmute::USELESS_TRANSMUTE), + LintId::of(unused_rounding::UNUSED_ROUNDING), LintId::of(use_self::USE_SELF), ]) diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 63232fd4113..48de92ae945 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -26,6 +26,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(doc::DOC_MARKDOWN), LintId::of(doc::MISSING_ERRORS_DOC), LintId::of(doc::MISSING_PANICS_DOC), + LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES), LintId::of(empty_enum::EMPTY_ENUM), LintId::of(enum_variants::MODULE_NAME_REPETITIONS), LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), @@ -50,8 +51,8 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(macro_use::MACRO_USE_IMPORTS), LintId::of(manual_assert::MANUAL_ASSERT), LintId::of(manual_ok_or::MANUAL_OK_OR), - LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(matches::MATCH_BOOL), + LintId::of(matches::MATCH_ON_VEC_ITEMS), LintId::of(matches::MATCH_SAME_ARMS), LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(matches::MATCH_WILD_ERR_ARM), @@ -66,6 +67,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(methods::UNNECESSARY_JOIN), LintId::of(misc::FLOAT_CMP), LintId::of(misc::USED_UNDERSCORE_BINDING), + LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER), LintId::of(mut_mut::MUT_MUT), LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL), LintId::of(needless_continue::NEEDLESS_CONTINUE), diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index a6d3a06dc16..3695012f552 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -3,9 +3,8 @@ // Manual edits will be overwritten. store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ - LintId::of(arithmetic::FLOAT_ARITHMETIC), - LintId::of(arithmetic::INTEGER_ARITHMETIC), LintId::of(as_conversions::AS_CONVERSIONS), + LintId::of(as_underscore::AS_UNDERSCORE), LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON), @@ -32,6 +31,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), LintId::of(map_err_ignore::MAP_ERR_IGNORE), LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), + LintId::of(matches::TRY_ERR), LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), LintId::of(mem_forget::MEM_FORGET), LintId::of(methods::CLONE_ON_REF_PTR), @@ -50,6 +50,8 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(module_style::MOD_MODULE_FILES), LintId::of(module_style::SELF_NAMED_MODULE_FILES), LintId::of(modulo_arithmetic::MODULO_ARITHMETIC), + LintId::of(numeric_arithmetic::FLOAT_ARITHMETIC), + LintId::of(numeric_arithmetic::INTEGER_ARITHMETIC), LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), LintId::of(panic_unimplemented::PANIC), LintId::of(panic_unimplemented::TODO), @@ -67,7 +69,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(strings::STRING_SLICE), LintId::of(strings::STRING_TO_STRING), LintId::of(strings::STR_TO_STRING), - LintId::of(try_err::TRY_ERR), LintId::of(types::RC_BUFFER), LintId::of(types::RC_MUTEX), LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 62f26d821a0..35575351784 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -12,7 +12,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(collapsible_match::COLLAPSIBLE_MATCH), LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), @@ -31,6 +30,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(functions::DOUBLE_MUST_USE), LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::RESULT_UNIT_ERR), + LintId::of(get_first::GET_FIRST), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(len_zero::COMPARISON_TO_EMPTY), @@ -45,11 +45,12 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_bits::MANUAL_BITS), - LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(map_clone::MAP_CLONE), LintId::of(match_result_ok::MATCH_RESULT_OK), + LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MANUAL_MAP), LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(matches::MATCH_OVERLAPPING_ARM), LintId::of(matches::MATCH_REF_PATS), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 2de49f1624a..7b13713c36e 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -3,6 +3,7 @@ // Manual edits will be overwritten. store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![ + LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), @@ -23,11 +24,13 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(loops::EMPTY_LOOP), LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::MUT_RANGE_BOUND), + LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE), + LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::SUSPICIOUS_MAP), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), - LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), + LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), ]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4ac834f7240..ee0416fc0ff 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -168,9 +168,10 @@ mod renamed_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absurd_extreme_comparisons; +mod almost_complete_letter_range; mod approx_const; -mod arithmetic; mod as_conversions; +mod as_underscore; mod asm_syntax; mod assertions_on_constants; mod assign_ops; @@ -183,6 +184,7 @@ mod blocks_in_if_conditions; mod bool_assert_comparison; mod booleans; mod borrow_as_ptr; +mod borrow_deref_ref; mod bytecount; mod bytes_count_to_len; mod cargo; @@ -191,7 +193,6 @@ mod casts; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; -mod collapsible_match; mod comparison_chain; mod copies; mod copy_iterator; @@ -208,6 +209,7 @@ mod disallowed_methods; mod disallowed_script_idents; mod disallowed_types; mod doc; +mod doc_link_with_quotes; mod double_comparison; mod double_parens; mod drop_forget_ref; @@ -242,7 +244,7 @@ mod from_over_into; mod from_str_radix_10; mod functions; mod future_not_send; -mod get_last_with_len; +mod get_first; mod identity_op; mod if_let_mutex; mod if_not_else; @@ -278,17 +280,13 @@ mod main_recursion; mod manual_assert; mod manual_async_fn; mod manual_bits; -mod manual_map; mod manual_non_exhaustive; mod manual_ok_or; mod manual_strip; -mod manual_unwrap_or; mod map_clone; mod map_err_ignore; mod map_unit_fn; -mod match_on_vec_items; mod match_result_ok; -mod match_str_case_mismatch; mod matches; mod mem_forget; mod mem_replace; @@ -296,6 +294,7 @@ mod methods; mod minmax; mod misc; mod misc_early; +mod mismatching_type_param_order; mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; @@ -328,6 +327,7 @@ mod non_expressive_names; mod non_octal_unix_permissions; mod non_send_fields_in_send_ty; mod nonstandard_macro_braces; +mod numeric_arithmetic; mod octal_escapes; mod only_used_in_recursion; mod open_options; @@ -367,7 +367,6 @@ mod self_named_constructors; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; -mod significant_drop_in_scrutinee; mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; @@ -378,6 +377,7 @@ mod strlen_on_c_strings; mod suspicious_operation_groupings; mod suspicious_trait_impl; mod swap; +mod swap_ptr_to_ref; mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; @@ -385,7 +385,6 @@ mod trailing_empty_array; mod trait_bounds; mod transmute; mod transmuting_null; -mod try_err; mod types; mod undocumented_unsafe_blocks; mod unicode; @@ -402,6 +401,7 @@ mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_async; mod unused_io_amount; +mod unused_rounding; mod unused_self; mod unused_unit; mod unwrap; @@ -562,7 +562,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(len_zero::LenZero)); store.register_late_pass(|| Box::new(attrs::Attributes)); store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions)); - store.register_late_pass(|| Box::new(collapsible_match::CollapsibleMatch)); store.register_late_pass(|| Box::new(unicode::Unicode)); store.register_late_pass(|| Box::new(uninit_vec::UninitVec)); store.register_late_pass(|| Box::new(unit_hash::UnitHash)); @@ -636,6 +635,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(mutex_atomic::Mutex)); store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate)); store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef)); + store.register_late_pass(|| Box::new(borrow_deref_ref::BorrowDerefRef)); store.register_late_pass(|| Box::new(no_effect::NoEffect)); store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment)); store.register_late_pass(|| Box::new(transmute::Transmute)); @@ -652,7 +652,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|| Box::new(derive::Derive)); store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls)); - store.register_late_pass(|| Box::new(get_last_with_len::GetLastWithLen)); store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef)); store.register_late_pass(|| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons)); @@ -678,7 +677,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone()))); store.register_late_pass(|| Box::new(neg_multiply::NegMultiply)); store.register_late_pass(|| Box::new(mem_forget::MemForget)); - store.register_late_pass(|| Box::new(arithmetic::Arithmetic::default())); + store.register_late_pass(|| Box::new(numeric_arithmetic::NumericArithmetic::default())); store.register_late_pass(|| Box::new(assign_ops::AssignOps)); store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq)); store.register_late_pass(|| Box::new(mixed_read_write_in_expression::EvalOrderDependence)); @@ -700,7 +699,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ); store.register_late_pass(move || Box::new(pass_by_ref_or_value)); store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef)); - store.register_late_pass(|| Box::new(try_err::TryErr)); store.register_late_pass(|| Box::new(bytecount::ByteCount)); store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter)); store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody)); @@ -812,7 +810,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(if_not_else::IfNotElse)); store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality)); store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock)); - store.register_late_pass(|| Box::new(match_on_vec_items::MatchOnVecItems)); store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn)); store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero)); store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn)); @@ -830,7 +827,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(repeat_once::RepeatOnce)); store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult)); store.register_late_pass(|| Box::new(self_assignment::SelfAssignment)); - store.register_late_pass(|| Box::new(manual_unwrap_or::ManualUnwrapOr)); store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr)); store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs)); store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); @@ -849,7 +845,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10)); - store.register_late_pass(|| Box::new(manual_map::ManualMap)); store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); @@ -875,7 +870,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); - store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch)); store.register_late_pass(move || Box::new(format_args::FormatArgs)); store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); @@ -886,9 +880,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv))); store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); + store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes)); store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion)); - store.register_late_pass(|| Box::new(significant_drop_in_scrutinee::SignificantDropInScrutinee)); - store.register_late_pass(|| Box::new(dbg_macro::DbgMacro)); + let allow_dbg_in_tests = conf.allow_dbg_in_tests; + store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; store.register_late_pass(move || { Box::new(cargo::Cargo { @@ -906,6 +901,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); + store.register_late_pass(|| Box::new(get_first::GetFirst)); + store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); + store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); + store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef)); + store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch)); + store.register_late_pass(|| Box::new(as_underscore::AsUnderscore)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 75d771f992a..d61be78895f 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -180,29 +180,24 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let opt = Some(1); - /// - /// // Bad + /// # let res: Result = Ok(1); /// for x in opt { /// // .. /// } /// - /// // Good - /// if let Some(x) = opt { + /// for x in &res { /// // .. /// } /// ``` /// - /// // or - /// + /// Use instead: /// ```rust + /// # let opt = Some(1); /// # let res: Result = Ok(1); - /// - /// // Bad - /// for x in &res { + /// if let Some(x) = opt { /// // .. /// } /// - /// // Good /// if let Ok(x) = res { /// // .. /// } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 4f85364965b..a348bb465c8 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -3,9 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{ - contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq, -}; +use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -29,7 +27,12 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { - if let Some(higher::Range { start: Some(start), ref end, limits }) = higher::Range::hir(arg) { + if let Some(higher::Range { + start: Some(start), + ref end, + limits, + }) = higher::Range::hir(arg) + { // the var must be a single name if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { let mut visitor = VarVisitor { @@ -104,22 +107,19 @@ pub(super) fn check<'tcx>( } } - if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) - { + if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { String::new() - } else if visitor.indexed_mut.contains(&indexed) - && contains_name(indexed, take_expr) - { + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { return; } else { match limits { ast::RangeLimits::Closed => { let take_expr = sugg::Sugg::hir(cx, take_expr, ""); format!(".take({})", take_expr + sugg::ONE) - } + }, ast::RangeLimits::HalfOpen => { format!(".take({})", snippet(cx, take_expr.span, "..")) - } + }, } } } else { @@ -145,10 +145,7 @@ pub(super) fn check<'tcx>( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!( - "the loop variable `{}` is used to index `{}`", - ident.name, indexed - ), + &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed), |diag| { multispan_sugg( diag, @@ -157,10 +154,7 @@ pub(super) fn check<'tcx>( (pat.span, format!("({}, )", ident.name)), ( arg.span, - format!( - "{}.{}().enumerate(){}{}", - indexed, method, method_1, method_2 - ), + format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2), ), ], ); @@ -177,10 +171,7 @@ pub(super) fn check<'tcx>( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!( - "the loop variable `{}` is only used to index `{}`", - ident.name, indexed - ), + &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed), |diag| { multispan_sugg( diag, @@ -257,12 +248,7 @@ struct VarVisitor<'a, 'tcx> { } impl<'a, 'tcx> VarVisitor<'a, 'tcx> { - fn check( - &mut self, - idx: &'tcx Expr<'_>, - seqexpr: &'tcx Expr<'_>, - expr: &'tcx Expr<'_>, - ) -> bool { + fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { if_chain! { // the indexed container is referenced by a name if let ExprKind::Path(ref seqpath) = seqexpr.kind; @@ -351,13 +337,13 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { self.visit_expr(lhs); self.prefer_mutable = false; self.visit_expr(rhs); - } + }, ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => { if mutbl == Mutability::Mut { self.prefer_mutable = true; } self.visit_expr(expr); - } + }, ExprKind::Call(f, args) => { self.visit_expr(f); for expr in args { @@ -370,11 +356,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { } self.visit_expr(expr); } - } + }, ExprKind::MethodCall(_, args, _) => { let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); - for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) - { + for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) { self.prefer_mutable = false; if let ty::Ref(_, _, mutbl) = *ty.kind() { if mutbl == Mutability::Mut { @@ -383,11 +368,11 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { } self.visit_expr(expr); } - } + }, ExprKind::Closure(_, _, body_id, ..) => { let body = self.cx.tcx.hir().body(body_id); self.visit_expr(&body.value); - } + }, _ => walk_expr(self, expr), } self.prefer_mutable = old; diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 70a118d6b35..c025f5972d5 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -146,7 +146,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { if arms.is_empty() { e } else { - let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id); + let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id); combine_seq(e, arms) } }, diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs deleted file mode 100644 index 230ae029ed9..00000000000 --- a/clippy_lints/src/manual_map.rs +++ /dev/null @@ -1,316 +0,0 @@ -use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::IfLetOrMatch; -use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; -use clippy_utils::{ - can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, - peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, -}; -use rustc_ast::util::parser::PREC_POSTFIX; -use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{ - def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, - QPath, UnsafeSource, -}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{sym, SyntaxContext}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usages of `match` which could be implemented using `map` - /// - /// ### Why is this bad? - /// Using the `map` method is clearer and more concise. - /// - /// ### Example - /// ```rust - /// match Some(0) { - /// Some(x) => Some(x + 1), - /// None => None, - /// }; - /// ``` - /// Use instead: - /// ```rust - /// Some(0).map(|x| x + 1); - /// ``` - #[clippy::version = "1.52.0"] - pub MANUAL_MAP, - style, - "reimplementation of `map`" -} - -declare_lint_pass!(ManualMap => [MANUAL_MAP]); - -impl<'tcx> LateLintPass<'tcx> for ManualMap { - #[expect(clippy::too_many_lines)] - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) { - Some(IfLetOrMatch::IfLet(scrutinee, pat, body, Some(r#else))) => (scrutinee, pat, body, None, r#else), - Some(IfLetOrMatch::Match( - scrutinee, - [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], - _, - )) => (scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body), - _ => return, - }; - if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { - return; - } - - let (scrutinee_ty, ty_ref_count, ty_mutability) = - peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) - { - return; - } - - let expr_ctxt = expr.span.ctxt(); - let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( - try_parse_pattern(cx, then_pat, expr_ctxt), - else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), - ) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, true) - }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, false) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, true) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, false) - }, - _ => return, - }; - - // Top level or patterns aren't allowed in closures. - if matches!(some_pat.kind, PatKind::Or(_)) { - return; - } - - let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) { - Some(expr) => expr, - None => return, - }; - - // These two lints will go back and forth with each other. - if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit - && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) - { - return; - } - - // `map` won't perform any adjustments. - if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { - return; - } - - // Determine which binding mode to use. - let explicit_ref = some_pat.contains_explicit_ref_binding(); - let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); - - let as_ref_str = match binding_ref { - Some(Mutability::Mut) => ".as_mut()", - Some(Mutability::Not) => ".as_ref()", - None => "", - }; - - match can_move_expr_to_closure(cx, some_expr.expr) { - Some(captures) => { - // Check if captures the closure will need conflict with borrows made in the scrutinee. - // TODO: check all the references made in the scrutinee expression. This will require interacting - // with the borrow checker. Currently only `[.]*` is checked for. - if let Some(binding_ref_mutability) = binding_ref { - let e = peel_hir_expr_while(scrutinee, |e| match e.kind { - ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { - match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, - Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { - return; - }, - Some(CaptureKind::Ref(Mutability::Not)) | None => (), - } - } - } - }, - None => return, - }; - - let mut app = Applicability::MachineApplicable; - - // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or - // it's being passed by value. - let scrutinee = peel_hir_expr_refs(scrutinee).0; - let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = - if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { - format!("({})", scrutinee_str) - } else { - scrutinee_str.into() - }; - - let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { - if_chain! { - if !some_expr.needs_unsafe_block; - if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); - if func.span.ctxt() == some_expr.expr.span.ctxt(); - then { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() - } else { - if path_to_local_id(some_expr.expr, id) - && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return; - } - - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::Mutable) { - "mut " - } else { - "" - }; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip) - } else { - format!("|{}{}| {}", annotation, some_binding, expr_snip) - } - } - } - } else if !is_wild_none && explicit_ref.is_none() { - // TODO: handle explicit reference annotations. - let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip) - } else { - format!("|{}| {}", pat_snip, expr_snip) - } - } else { - // Refutable bindings and mixed reference annotations can't be handled by `map`. - return; - }; - - span_lint_and_sugg( - cx, - MANUAL_MAP, - expr.span, - "manual implementation of `Option::map`", - "try this", - if else_pat.is_none() && is_else_clause(cx.tcx, expr) { - format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) - } else { - format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) - }, - app, - ); - } -} - -// Checks whether the expression could be passed as a function, or whether a closure is needed. -// Returns the function to be passed to `map` if it exists. -fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - match expr.kind { - ExprKind::Call(func, [arg]) - if path_to_local_id(arg, binding) - && cx.typeck_results().expr_adjustments(arg).is_empty() - && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => - { - Some(func) - }, - _ => None, - } -} - -enum OptionPat<'a> { - Wild, - None, - Some { - // The pattern contained in the `Some` tuple. - pattern: &'a Pat<'a>, - // The number of references before the `Some` tuple. - // e.g. `&&Some(_)` has a ref count of 2. - ref_count: usize, - }, -} - -struct SomeExpr<'tcx> { - expr: &'tcx Expr<'tcx>, - needs_unsafe_block: bool, -} - -// Try to parse into a recognized `Option` pattern. -// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { - fn f<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - ref_count: usize, - ctxt: SyntaxContext, - ) -> Option> { - match pat.kind { - PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), - PatKind::TupleStruct(ref qpath, [pattern], _) - if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => - { - Some(OptionPat::Some { pattern, ref_count }) - }, - _ => None, - } - } - f(cx, pat, 0, ctxt) -} - -// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. -fn get_some_expr<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - needs_unsafe_block: bool, - ctxt: SyntaxContext, -) -> Option> { - // TODO: Allow more complex expressions. - match expr.kind { - ExprKind::Call( - Expr { - kind: ExprKind::Path(ref qpath), - .. - }, - [arg], - ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr { - expr: arg, - needs_unsafe_block, - }), - ExprKind::Block( - Block { - stmts: [], - expr: Some(expr), - rules, - .. - }, - _, - ) => get_some_expr( - cx, - expr, - needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), - ctxt, - ), - _ => None, - } -} - -// Checks for the `None` value. -fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) -} diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 80845ace3f9..14f5faafd7c 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -113,7 +113,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { let mut iter = fields.iter().filter_map(|f| match f.vis.kind { VisibilityKind::Public => None, VisibilityKind::Inherited => Some(Ok(f)), - _ => Some(Err(())), + VisibilityKind::Restricted { .. } => Some(Err(())), }); if let Some(Ok(field)) = iter.next() && iter.next().is_none() diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs deleted file mode 100644 index b3a91d9f18f..00000000000 --- a/clippy_lints/src/manual_unwrap_or.rs +++ /dev/null @@ -1,123 +0,0 @@ -use clippy_utils::consts::constant_simple; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{in_constant, is_lang_ctor, path_to_local_id, sugg}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; -use rustc_hir::{Arm, Expr, ExprKind, PatKind}; -use rustc_lint::LintContext; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`. - /// - /// ### Why is this bad? - /// Concise code helps focusing on behavior instead of boilerplate. - /// - /// ### Example - /// ```rust - /// let foo: Option = None; - /// match foo { - /// Some(v) => v, - /// None => 1, - /// }; - /// ``` - /// - /// Use instead: - /// ```rust - /// let foo: Option = None; - /// foo.unwrap_or(1); - /// ``` - #[clippy::version = "1.49.0"] - pub MANUAL_UNWRAP_OR, - complexity, - "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`" -} - -declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); - -impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOr { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { - return; - } - lint_manual_unwrap_or(cx, expr); - } -} - -fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { - if_chain! { - if arms.len() == 2; - if arms.iter().all(|arm| arm.guard.is_none()); - if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { - match arm.pat.kind { - PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), - PatKind::TupleStruct(ref qpath, [pat], _) => - matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr), - _ => false, - } - }); - let unwrap_arm = &arms[1 - idx]; - if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind; - if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); - if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; - if path_to_local_id(unwrap_arm.body, binding_hir_id); - if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); - if !contains_return_break_continue_macro(or_arm.body); - then { - Some(or_arm) - } else { - None - } - } - } - - if_chain! { - if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; - let ty = cx.typeck_results().expr_ty(scrutinee); - if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) { - Some("Option") - } else if is_type_diagnostic_item(cx, ty, sym::Result) { - Some("Result") - } else { - None - }; - if let Some(or_arm) = applicable_or_arm(cx, match_arms); - if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span); - if let Some(indent) = indent_of(cx, expr.span); - if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); - then { - let reindented_or_body = - reindent_multiline(or_body_snippet.into(), true, Some(indent)); - - let suggestion = if scrutinee.span.from_expansion() { - // we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)` - sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..") - } - else { - sugg::Sugg::hir(cx, scrutinee, "..").maybe_par() - }; - - span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `{}::unwrap_or`", ty_name), - "replace with", - format!( - "{}.unwrap_or({})", - suggestion, - reindented_or_body, - ), - Applicability::MachineApplicable, - ); - } - } -} diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs deleted file mode 100644 index 583b577ffe2..00000000000 --- a/clippy_lints/src/match_on_vec_items.rs +++ /dev/null @@ -1,104 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, MatchSource}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for `match vec[idx]` or `match vec[n..m]`. - /// - /// ### Why is this bad? - /// This can panic at runtime. - /// - /// ### Example - /// ```rust, no_run - /// let arr = vec![0, 1, 2, 3]; - /// let idx = 1; - /// - /// // Bad - /// match arr[idx] { - /// 0 => println!("{}", 0), - /// 1 => println!("{}", 3), - /// _ => {}, - /// } - /// ``` - /// Use instead: - /// ```rust, no_run - /// let arr = vec![0, 1, 2, 3]; - /// let idx = 1; - /// - /// // Good - /// match arr.get(idx) { - /// Some(0) => println!("{}", 0), - /// Some(1) => println!("{}", 3), - /// _ => {}, - /// } - /// ``` - #[clippy::version = "1.45.0"] - pub MATCH_ON_VEC_ITEMS, - pedantic, - "matching on vector elements can panic" -} - -declare_lint_pass!(MatchOnVecItems => [MATCH_ON_VEC_ITEMS]); - -impl<'tcx> LateLintPass<'tcx> for MatchOnVecItems { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Match(match_expr, _, MatchSource::Normal) = expr.kind; - if let Some(idx_expr) = is_vec_indexing(cx, match_expr); - if let ExprKind::Index(vec, idx) = idx_expr.kind; - - then { - // FIXME: could be improved to suggest surrounding every pattern with Some(_), - // but only when `or_patterns` are stabilized. - span_lint_and_sugg( - cx, - MATCH_ON_VEC_ITEMS, - match_expr.span, - "indexing into a vector may panic", - "try this", - format!( - "{}.get({})", - snippet(cx, vec.span, ".."), - snippet(cx, idx.span, "..") - ), - Applicability::MaybeIncorrect - ); - } - } - } -} - -fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if_chain! { - if let ExprKind::Index(array, index) = expr.kind; - if is_vector(cx, array); - if !is_full_range(cx, index); - - then { - return Some(expr); - } - } - - None -} - -fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); - let ty = ty.peel_refs(); - is_type_diagnostic_item(cx, ty, sym::Vec) -} - -fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); - let ty = ty.peel_refs(); - is_type_lang_item(cx, ty, LangItem::RangeFull) -} diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs similarity index 71% rename from clippy_lints/src/collapsible_match.rs rename to clippy_lints/src/matches/collapsible_match.rs index ec55009f347..07021f1bcad 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -6,68 +6,28 @@ use if_chain::if_chain; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_lint::LateContext; use rustc_span::Span; -declare_clippy_lint! { - /// ### What it does - /// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together - /// without adding any branches. - /// - /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only - /// cases where merging would most likely make the code more readable. - /// - /// ### Why is this bad? - /// It is unnecessarily verbose and complex. - /// - /// ### Example - /// ```rust - /// fn func(opt: Option>) { - /// let n = match opt { - /// Some(n) => match n { - /// Ok(n) => n, - /// _ => return, - /// } - /// None => return, - /// }; - /// } - /// ``` - /// Use instead: - /// ```rust - /// fn func(opt: Option>) { - /// let n = match opt { - /// Some(Ok(n)) => n, - /// _ => return, - /// }; - /// } - /// ``` - #[clippy::version = "1.50.0"] - pub COLLAPSIBLE_MATCH, - style, - "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together." -} +use super::COLLAPSIBLE_MATCH; -declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]); - -impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - match IfLetOrMatch::parse(cx, expr) { - Some(IfLetOrMatch::Match(_, arms, _)) => { - if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { - for arm in arms { - check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body)); - } - } - }, - Some(IfLetOrMatch::IfLet(_, pat, body, els)) => { - check_arm(cx, false, pat, body, None, els); - }, - None => {}, +pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { + if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { + for arm in arms { + check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body)); } } } +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + body: &'tcx Expr<'_>, + else_expr: Option<&'tcx Expr<'_>>, +) { + check_arm(cx, false, pat, body, None, else_expr); +} + fn check_arm<'tcx>( cx: &LateContext<'tcx>, outer_is_match: bool, diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs new file mode 100644 index 00000000000..542905a2d76 --- /dev/null +++ b/clippy_lints/src/matches/manual_map.rs @@ -0,0 +1,306 @@ +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::{ + can_move_expr_to_closure, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, peel_blocks, + peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, +}; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::{ + def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, + QPath, UnsafeSource, +}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +use super::MANUAL_MAP; + +pub(super) fn check_match<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], +) { + if let [arm1, arm2] = arms + && arm1.guard.is_none() + && arm2.guard.is_none() + { + check(cx, expr, scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body); + } +} + +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + let_pat: &'tcx Pat<'_>, + let_expr: &'tcx Expr<'_>, + then_expr: &'tcx Expr<'_>, + else_expr: &'tcx Expr<'_>, +) { + check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); +} + +#[expect(clippy::too_many_lines)] +fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, +) { + let (scrutinee_ty, ty_ref_count, ty_mutability) = + peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) + { + return; + } + + let expr_ctxt = expr.span.ctxt(); + let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( + try_parse_pattern(cx, then_pat, expr_ctxt), + else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), + ) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, false) + }, + _ => return, + }; + + // Top level or patterns aren't allowed in closures. + if matches!(some_pat.kind, PatKind::Or(_)) { + return; + } + + let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) { + Some(expr) => expr, + None => return, + }; + + // These two lints will go back and forth with each other. + if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit + && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) + { + return; + } + + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { + return; + } + + // Determine which binding mode to use. + let explicit_ref = some_pat.contains_explicit_ref_binding(); + let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); + + let as_ref_str = match binding_ref { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", + }; + + match can_move_expr_to_closure(cx, some_expr.expr) { + Some(captures) => { + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if let Some(binding_ref_mutability) = binding_ref { + let e = peel_hir_expr_while(scrutinee, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match captures.get(l) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, + Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { + return; + }, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } + } + } + }, + None => return, + }; + + let mut app = Applicability::MachineApplicable; + + // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or + // it's being passed by value. + let scrutinee = peel_hir_expr_refs(scrutinee).0; + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { + format!("({})", scrutinee_str) + } else { + scrutinee_str.into() + }; + + let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { + if_chain! { + if !some_expr.needs_unsafe_block; + if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); + if func.span.ctxt() == some_expr.expr.span.ctxt(); + then { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + } else { + if path_to_local_id(some_expr.expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return; + } + + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::Mutable) { + "mut " + } else { + "" + }; + let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { + format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip) + } else { + format!("|{}{}| {}", annotation, some_binding, expr_snip) + } + } + } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; + let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { + format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip) + } else { + format!("|{}| {}", pat_snip, expr_snip) + } + } else { + // Refutable bindings and mixed reference annotations can't be handled by `map`. + return; + }; + + span_lint_and_sugg( + cx, + MANUAL_MAP, + expr.span, + "manual implementation of `Option::map`", + "try this", + if else_pat.is_none() && is_else_clause(cx.tcx, expr) { + format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) + } else { + format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) + }, + app, + ); +} + +// Checks whether the expression could be passed as a function, or whether a closure is needed. +// Returns the function to be passed to `map` if it exists. +fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + match expr.kind { + ExprKind::Call(func, [arg]) + if path_to_local_id(arg, binding) + && cx.typeck_results().expr_adjustments(arg).is_empty() + && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => + { + Some(func) + }, + _ => None, + } +} + +enum OptionPat<'a> { + Wild, + None, + Some { + // The pattern contained in the `Some` tuple. + pattern: &'a Pat<'a>, + // The number of references before the `Some` tuple. + // e.g. `&&Some(_)` has a ref count of 2. + ref_count: usize, + }, +} + +struct SomeExpr<'tcx> { + expr: &'tcx Expr<'tcx>, + needs_unsafe_block: bool, +} + +// Try to parse into a recognized `Option` pattern. +// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. +fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { + fn f<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ref_count: usize, + ctxt: SyntaxContext, + ) -> Option> { + match pat.kind { + PatKind::Wild => Some(OptionPat::Wild), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), + PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), + PatKind::TupleStruct(ref qpath, [pattern], _) + if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => + { + Some(OptionPat::Some { pattern, ref_count }) + }, + _ => None, + } + } + f(cx, pat, 0, ctxt) +} + +// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. +fn get_some_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + needs_unsafe_block: bool, + ctxt: SyntaxContext, +) -> Option> { + // TODO: Allow more complex expressions. + match expr.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(ref qpath), + .. + }, + [arg], + ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr { + expr: arg, + needs_unsafe_block, + }), + ExprKind::Block( + Block { + stmts: [], + expr: Some(expr), + rules, + .. + }, + _, + ) => get_some_expr( + cx, + expr, + needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + ctxt, + ), + _ => None, + } +} + +// Checks for the `None` value. +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) +} diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs new file mode 100644 index 00000000000..e1111c80f2f --- /dev/null +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -0,0 +1,83 @@ +use clippy_utils::consts::constant_simple; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::usage::contains_return_break_continue_macro; +use clippy_utils::{is_lang_ctor, path_to_local_id, sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::{Arm, Expr, PatKind}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::MANUAL_UNWRAP_OR; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { + let ty = cx.typeck_results().expr_ty(scrutinee); + if_chain! { + if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) { + Some("Option") + } else if is_type_diagnostic_item(cx, ty, sym::Result) { + Some("Result") + } else { + None + }; + if let Some(or_arm) = applicable_or_arm(cx, arms); + if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span); + if let Some(indent) = indent_of(cx, expr.span); + if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); + then { + let reindented_or_body = + reindent_multiline(or_body_snippet.into(), true, Some(indent)); + + let suggestion = if scrutinee.span.from_expansion() { + // we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)` + sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..") + } + else { + sugg::Sugg::hir(cx, scrutinee, "..").maybe_par() + }; + + span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR, expr.span, + &format!("this pattern reimplements `{}::unwrap_or`", ty_name), + "replace with", + format!( + "{}.unwrap_or({})", + suggestion, + reindented_or_body, + ), + Applicability::MachineApplicable, + ); + } + } +} + +fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { + if_chain! { + if arms.len() == 2; + if arms.iter().all(|arm| arm.guard.is_none()); + if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { + match arm.pat.kind { + PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::TupleStruct(ref qpath, [pat], _) => + matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr), + _ => false, + } + }); + let unwrap_arm = &arms[1 - idx]; + if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind; + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); + if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; + if path_to_local_id(unwrap_arm.body, binding_hir_id); + if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); + if !contains_return_break_continue_macro(or_arm.body); + then { + Some(or_arm) + } else { + None + } + } +} diff --git a/clippy_lints/src/matches/match_bool.rs b/clippy_lints/src/matches/match_bool.rs index 90c50b994d2..1c216e13570 100644 --- a/clippy_lints/src/matches/match_bool.rs +++ b/clippy_lints/src/matches/match_bool.rs @@ -24,8 +24,8 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { if let ExprKind::Lit(ref lit) = arm_bool.kind { match lit.node { - LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)), - LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)), + LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), + LitKind::Bool(false) => Some((arms[1].body, arms[0].body)), _ => None, } } else { diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 2e1f7646eb4..a68eec842ab 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_wild; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{higher, is_wild}; use rustc_ast::{Attribute, LitKind}; use rustc_errors::Applicability; use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat}; @@ -11,22 +11,24 @@ use rustc_span::source_map::Spanned; use super::MATCH_LIKE_MATCHES_MACRO; /// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(higher::IfLet { - let_pat, +pub(crate) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + let_pat: &'tcx Pat<'_>, + let_expr: &'tcx Expr<'_>, + then_expr: &'tcx Expr<'_>, + else_expr: &'tcx Expr<'_>, +) { + find_matches_sugg( + cx, let_expr, - if_then, - if_else: Some(if_else), - }) = higher::IfLet::hir(cx, expr) - { - find_matches_sugg( - cx, - let_expr, - IntoIterator::into_iter([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]), - expr, - true, - ); - } + IntoIterator::into_iter([ + (&[][..], Some(let_pat), then_expr, None), + (&[][..], None, else_expr, None), + ]), + expr, + true, + ); } pub(super) fn check_match<'tcx>( diff --git a/clippy_lints/src/matches/match_on_vec_items.rs b/clippy_lints/src/matches/match_on_vec_items.rs new file mode 100644 index 00000000000..2917f85c45f --- /dev/null +++ b/clippy_lints/src/matches/match_on_vec_items.rs @@ -0,0 +1,61 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::MATCH_ON_VEC_ITEMS; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) { + if_chain! { + if let Some(idx_expr) = is_vec_indexing(cx, scrutinee); + if let ExprKind::Index(vec, idx) = idx_expr.kind; + + then { + // FIXME: could be improved to suggest surrounding every pattern with Some(_), + // but only when `or_patterns` are stabilized. + span_lint_and_sugg( + cx, + MATCH_ON_VEC_ITEMS, + scrutinee.span, + "indexing into a vector may panic", + "try this", + format!( + "{}.get({})", + snippet(cx, vec.span, ".."), + snippet(cx, idx.span, "..") + ), + Applicability::MaybeIncorrect + ); + } + } +} + +fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if_chain! { + if let ExprKind::Index(array, index) = expr.kind; + if is_vector(cx, array); + if !is_full_range(cx, index); + + then { + return Some(expr); + } + } + + None +} + +fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + let ty = ty.peel_refs(); + is_type_diagnostic_item(cx, ty, sym::Vec) +} + +fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + let ty = ty.peel_refs(); + is_type_lang_item(cx, ty, LangItem::RangeFull) +} diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs similarity index 64% rename from clippy_lints/src/match_str_case_mismatch.rs rename to clippy_lints/src/matches/match_str_case_mismatch.rs index 85aec93670b..8302ce426e5 100644 --- a/clippy_lints/src/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -3,48 +3,13 @@ use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; +use rustc_hir::{Arm, Expr, ExprKind, PatKind}; +use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; -declare_clippy_lint! { - /// ### What it does - /// Checks for `match` expressions modifying the case of a string with non-compliant arms - /// - /// ### Why is this bad? - /// The arm is unreachable, which is likely a mistake - /// - /// ### Example - /// ```rust - /// # let text = "Foo"; - /// - /// match &*text.to_ascii_lowercase() { - /// "foo" => {}, - /// "Bar" => {}, - /// _ => {}, - /// } - /// ``` - /// Use instead: - /// ```rust - /// # let text = "Foo"; - /// - /// match &*text.to_ascii_lowercase() { - /// "foo" => {}, - /// "bar" => {}, - /// _ => {}, - /// } - /// ``` - #[clippy::version = "1.58.0"] - pub MATCH_STR_CASE_MISMATCH, - correctness, - "creation of a case altering match expression with non-compliant arms" -} - -declare_lint_pass!(MatchStrCaseMismatch => [MATCH_STR_CASE_MISMATCH]); +use super::MATCH_STR_CASE_MISMATCH; #[derive(Debug)] enum CaseMethod { @@ -54,25 +19,21 @@ enum CaseMethod { AsciiUppercase, } -impl<'tcx> LateLintPass<'tcx> for MatchStrCaseMismatch { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !in_external_macro(cx.tcx.sess, expr.span); - if let ExprKind::Match(match_expr, arms, MatchSource::Normal) = expr.kind; - if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(match_expr).kind(); - if let ty::Str = ty.kind(); - then { - let mut visitor = MatchExprVisitor { - cx, - case_method: None, - }; +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { + if_chain! { + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind(); + if let ty::Str = ty.kind(); + then { + let mut visitor = MatchExprVisitor { + cx, + case_method: None, + }; - visitor.visit_expr(match_expr); + visitor.visit_expr(scrutinee); - if let Some(case_method) = visitor.case_method { - if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { - lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); - } + if let Some(case_method) = visitor.case_method { + if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { + lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); } } } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 3d8391bce2b..d1e42f39e47 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,26 +1,34 @@ use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context}; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::{higher, in_constant, meets_msrv, msrvs}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, SpanData, SyntaxContext}; +mod collapsible_match; mod infallible_destructuring_match; +mod manual_map; +mod manual_unwrap_or; mod match_as_ref; mod match_bool; mod match_like_matches; +mod match_on_vec_items; mod match_ref_pats; mod match_same_arms; mod match_single_binding; +mod match_str_case_mismatch; mod match_wild_enum; mod match_wild_err_arm; mod needless_match; mod overlapping_arms; mod redundant_pattern_match; mod rest_pat_in_fully_bound_struct; +mod significant_drop_in_scrutinee; mod single_match; +mod try_err; mod wild_in_or_pats; declare_clippy_lint! { @@ -610,6 +618,274 @@ declare_clippy_lint! { "`match` or match-like `if let` that are unnecessary" } +declare_clippy_lint! { + /// ### What it does + /// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together + /// without adding any branches. + /// + /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only + /// cases where merging would most likely make the code more readable. + /// + /// ### Why is this bad? + /// It is unnecessarily verbose and complex. + /// + /// ### Example + /// ```rust + /// fn func(opt: Option>) { + /// let n = match opt { + /// Some(n) => match n { + /// Ok(n) => n, + /// _ => return, + /// } + /// None => return, + /// }; + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn func(opt: Option>) { + /// let n = match opt { + /// Some(Ok(n)) => n, + /// _ => return, + /// }; + /// } + /// ``` + #[clippy::version = "1.50.0"] + pub COLLAPSIBLE_MATCH, + style, + "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together." +} + +declare_clippy_lint! { + /// ### What it does + /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`. + /// + /// ### Why is this bad? + /// Concise code helps focusing on behavior instead of boilerplate. + /// + /// ### Example + /// ```rust + /// let foo: Option = None; + /// match foo { + /// Some(v) => v, + /// None => 1, + /// }; + /// ``` + /// + /// Use instead: + /// ```rust + /// let foo: Option = None; + /// foo.unwrap_or(1); + /// ``` + #[clippy::version = "1.49.0"] + pub MANUAL_UNWRAP_OR, + complexity, + "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `match vec[idx]` or `match vec[n..m]`. + /// + /// ### Why is this bad? + /// This can panic at runtime. + /// + /// ### Example + /// ```rust, no_run + /// let arr = vec![0, 1, 2, 3]; + /// let idx = 1; + /// + /// // Bad + /// match arr[idx] { + /// 0 => println!("{}", 0), + /// 1 => println!("{}", 3), + /// _ => {}, + /// } + /// ``` + /// Use instead: + /// ```rust, no_run + /// let arr = vec![0, 1, 2, 3]; + /// let idx = 1; + /// + /// // Good + /// match arr.get(idx) { + /// Some(0) => println!("{}", 0), + /// Some(1) => println!("{}", 3), + /// _ => {}, + /// } + /// ``` + #[clippy::version = "1.45.0"] + pub MATCH_ON_VEC_ITEMS, + pedantic, + "matching on vector elements can panic" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `match` expressions modifying the case of a string with non-compliant arms + /// + /// ### Why is this bad? + /// The arm is unreachable, which is likely a mistake + /// + /// ### Example + /// ```rust + /// # let text = "Foo"; + /// match &*text.to_ascii_lowercase() { + /// "foo" => {}, + /// "Bar" => {}, + /// _ => {}, + /// } + /// ``` + /// Use instead: + /// ```rust + /// # let text = "Foo"; + /// match &*text.to_ascii_lowercase() { + /// "foo" => {}, + /// "bar" => {}, + /// _ => {}, + /// } + /// ``` + #[clippy::version = "1.58.0"] + pub MATCH_STR_CASE_MISMATCH, + correctness, + "creation of a case altering match expression with non-compliant arms" +} + +declare_clippy_lint! { + /// ### What it does + /// Check for temporaries returned from function calls in a match scrutinee that have the + /// `clippy::has_significant_drop` attribute. + /// + /// ### Why is this bad? + /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have + /// an important side-effect, such as unlocking a mutex, making it important for users to be + /// able to accurately understand their lifetimes. When a temporary is returned in a function + /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may + /// be surprising. + /// + /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a + /// function call that returns a `MutexGuard` and then tries to lock again in one of the match + /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of + /// the match block and thus will not unlock. + /// + /// ### Example + /// ```rust.ignore + /// # use std::sync::Mutex; + /// + /// # struct State {} + /// + /// # impl State { + /// # fn foo(&self) -> bool { + /// # true + /// # } + /// + /// # fn bar(&self) {} + /// # } + /// + /// + /// let mutex = Mutex::new(State {}); + /// + /// match mutex.lock().unwrap().foo() { + /// true => { + /// mutex.lock().unwrap().bar(); // Deadlock! + /// } + /// false => {} + /// }; + /// + /// println!("All done!"); + /// + /// ``` + /// Use instead: + /// ```rust + /// # use std::sync::Mutex; + /// + /// # struct State {} + /// + /// # impl State { + /// # fn foo(&self) -> bool { + /// # true + /// # } + /// + /// # fn bar(&self) {} + /// # } + /// + /// let mutex = Mutex::new(State {}); + /// + /// let is_foo = mutex.lock().unwrap().foo(); + /// match is_foo { + /// true => { + /// mutex.lock().unwrap().bar(); + /// } + /// false => {} + /// }; + /// + /// println!("All done!"); + /// ``` + #[clippy::version = "1.60.0"] + pub SIGNIFICANT_DROP_IN_SCRUTINEE, + suspicious, + "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `Err(x)?`. + /// + /// ### Why is this bad? + /// The `?` operator is designed to allow calls that + /// can fail to be easily chained. For example, `foo()?.bar()` or + /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will + /// always return), it is more clear to write `return Err(x)`. + /// + /// ### Example + /// ```rust + /// fn foo(fail: bool) -> Result { + /// if fail { + /// Err("failed")?; + /// } + /// Ok(0) + /// } + /// ``` + /// Could be written: + /// + /// ```rust + /// fn foo(fail: bool) -> Result { + /// if fail { + /// return Err("failed".into()); + /// } + /// Ok(0) + /// } + /// ``` + #[clippy::version = "1.38.0"] + pub TRY_ERR, + restriction, + "return errors explicitly rather than hiding them behind a `?`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `match` which could be implemented using `map` + /// + /// ### Why is this bad? + /// Using the `map` method is clearer and more concise. + /// + /// ### Example + /// ```rust + /// match Some(0) { + /// Some(x) => Some(x + 1), + /// None => None, + /// }; + /// ``` + /// Use instead: + /// ```rust + /// Some(0).map(|x| x + 1); + /// ``` + #[clippy::version = "1.52.0"] + pub MANUAL_MAP, + style, + "reimplementation of `map`" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -644,19 +920,42 @@ impl_lint_pass!(Matches => [ MATCH_LIKE_MATCHES_MACRO, MATCH_SAME_ARMS, NEEDLESS_MATCH, + COLLAPSIBLE_MATCH, + MANUAL_UNWRAP_OR, + MATCH_ON_VEC_ITEMS, + MATCH_STR_CASE_MISMATCH, + SIGNIFICANT_DROP_IN_SCRUTINEE, + TRY_ERR, + MANUAL_MAP, ]); impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { + if in_external_macro(cx.sess(), expr.span) { return; } + let from_expansion = expr.span.from_expansion(); if let ExprKind::Match(ex, arms, source) = expr.kind { - if !span_starts_with(cx, expr.span, "match") { + if source == MatchSource::Normal && !span_starts_with(cx, expr.span, "match") { return; } - if !contains_cfg_arm(cx, expr, ex, arms) { + if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) { + significant_drop_in_scrutinee::check(cx, expr, ex, source); + } + + collapsible_match::check_match(cx, arms); + if !from_expansion { + // These don't depend on a relationship between multiple arms + match_wild_err_arm::check(cx, ex, arms); + wild_in_or_pats::check(cx, arms); + } + + if source == MatchSource::TryDesugar { + try_err::check(cx, expr, ex); + } + + if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) @@ -671,6 +970,13 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_wild_enum::check(cx, ex, arms); match_as_ref::check(cx, ex, arms, expr); needless_match::check_match(cx, ex, arms, expr); + match_on_vec_items::check(cx, ex); + match_str_case_mismatch::check(cx, ex, arms); + + if !in_constant(cx, expr.hir_id) { + manual_unwrap_or::check(cx, expr, ex, arms); + manual_map::check_match(cx, expr, ex, arms); + } if self.infallible_destructuring_match_linted { self.infallible_destructuring_match_linted = false; @@ -680,16 +986,35 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr); } - - // These don't depend on a relationship between multiple arms - match_wild_err_arm::check(cx, ex, arms); - wild_in_or_pats::check(cx, arms); - } else { - if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { - match_like_matches::check(cx, expr); + } else if let Some(if_let) = higher::IfLet::hir(cx, expr) { + collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else); + if !from_expansion { + if let Some(else_expr) = if_let.if_else { + if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { + match_like_matches::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_then, + else_expr, + ); + } + if !in_constant(cx, expr.hir_id) { + manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr); + } + } + redundant_pattern_match::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_else.is_some(), + ); + needless_match::check_if_let(cx, expr, &if_let); } + } else if !from_expansion { redundant_pattern_match::check(cx, expr); - needless_match::check(cx, expr); } } diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index f920ad4651f..fa19cddd35e 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -47,20 +47,18 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], /// some_enum /// } /// ``` -pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) { - if let Some(ref if_let) = higher::IfLet::hir(cx, ex) { - if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - NEEDLESS_MATCH, - ex.span, - "this if-let expression is unnecessary", - "replace it with", - snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(), - applicability, - ); - } +pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let: &higher::IfLet<'tcx>) { + if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let_inner(cx, if_let) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + NEEDLESS_MATCH, + ex.span, + "this if-let expression is unnecessary", + "replace it with", + snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(), + applicability, + ); } } @@ -77,7 +75,7 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) true } -fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { +fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if let Some(if_else) = if_let.if_else { if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) { return false; @@ -85,7 +83,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { // Recursively check for each `else if let` phrase, if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) { - return check_if_let(cx, nested_if_let); + return check_if_let_inner(cx, nested_if_let); } if matches!(if_else.kind, ExprKind::Block(..)) { diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 1a8b9d15f37..095cd43ea13 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -18,19 +18,21 @@ use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty}; use rustc_span::sym; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(higher::IfLet { - if_else, - let_pat, - let_expr, - .. - }) = higher::IfLet::hir(cx, expr) - { - find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some()); - } else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) { + if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) { find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false); } } +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + pat: &'tcx Pat<'_>, + scrutinee: &'tcx Expr<'_>, + has_else: bool, +) { + find_sugg_for_if_let(cx, expr, pat, scrutinee, "if", has_else); +} + // Extract the generic arguments out of a type fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { if_chain! { diff --git a/clippy_lints/src/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs similarity index 64% rename from clippy_lints/src/significant_drop_in_scrutinee.rs rename to clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 424b361a905..a211dc18f9e 100644 --- a/clippy_lints/src/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -4,103 +4,25 @@ use clippy_utils::get_attr; use clippy_utils::source::{indent_of, snippet}; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_hir::{Expr, ExprKind, MatchSource}; +use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{Ty, TypeAndMut}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; -declare_clippy_lint! { - /// ### What it does - /// Check for temporaries returned from function calls in a match scrutinee that have the - /// `clippy::has_significant_drop` attribute. - /// - /// ### Why is this bad? - /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have - /// an important side-effect, such as unlocking a mutex, making it important for users to be - /// able to accurately understand their lifetimes. When a temporary is returned in a function - /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may - /// be surprising. - /// - /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a - /// function call that returns a `MutexGuard` and then tries to lock again in one of the match - /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of - /// the match block and thus will not unlock. - /// - /// ### Example - /// ```rust.ignore - /// # use std::sync::Mutex; - /// - /// # struct State {} - /// - /// # impl State { - /// # fn foo(&self) -> bool { - /// # true - /// # } - /// - /// # fn bar(&self) {} - /// # } - /// - /// - /// let mutex = Mutex::new(State {}); - /// - /// match mutex.lock().unwrap().foo() { - /// true => { - /// mutex.lock().unwrap().bar(); // Deadlock! - /// } - /// false => {} - /// }; - /// - /// println!("All done!"); - /// - /// ``` - /// Use instead: - /// ```rust - /// # use std::sync::Mutex; - /// - /// # struct State {} - /// - /// # impl State { - /// # fn foo(&self) -> bool { - /// # true - /// # } - /// - /// # fn bar(&self) {} - /// # } - /// - /// let mutex = Mutex::new(State {}); - /// - /// let is_foo = mutex.lock().unwrap().foo(); - /// match is_foo { - /// true => { - /// mutex.lock().unwrap().bar(); - /// } - /// false => {} - /// }; - /// - /// println!("All done!"); - /// ``` - #[clippy::version = "1.60.0"] - pub SIGNIFICANT_DROP_IN_SCRUTINEE, - suspicious, - "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime" -} +use super::SIGNIFICANT_DROP_IN_SCRUTINEE; -declare_lint_pass!(SignificantDropInScrutinee => [SIGNIFICANT_DROP_IN_SCRUTINEE]); - -impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some(suggestions) = has_significant_drop_in_scrutinee(cx, expr) { - for found in suggestions { - span_lint_and_then( - cx, - SIGNIFICANT_DROP_IN_SCRUTINEE, - found.found_span, - "temporary with significant drop in match scrutinee", - |diag| set_diagnostic(diag, cx, expr, found), - ); - } +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + scrutinee: &'tcx Expr<'_>, + source: MatchSource, +) { + if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) { + for found in suggestions { + span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| { + set_diagnostic(diag, cx, expr, found); + }); } } } @@ -152,13 +74,18 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t /// may have a surprising lifetime. fn has_significant_drop_in_scrutinee<'tcx, 'a>( cx: &'a LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, -) -> Option> { + scrutinee: &'tcx Expr<'tcx>, + source: MatchSource, +) -> Option<(Vec, &'static str)> { let mut helper = SigDropHelper::new(cx); - match expr.kind { - ExprKind::Match(match_expr, _, _) => helper.find_sig_drop(match_expr), - _ => None, - } + helper.find_sig_drop(scrutinee).map(|drops| { + let message = if source == MatchSource::Normal { + "temporary with significant drop in match scrutinee" + } else { + "temporary with significant drop in for loop" + }; + (drops, message) + }) } struct SigDropHelper<'a, 'tcx> { @@ -213,6 +140,19 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { self.sig_drop_spans.take() } + fn replace_current_sig_drop( + &mut self, + found_span: Span, + is_unit_return_val: bool, + lint_suggestion: LintSuggestion, + ) { + self.current_sig_drop.replace(FoundSigDrop { + found_span, + is_unit_return_val, + lint_suggestion, + }); + } + /// This will try to set the current suggestion (so it can be moved into the suggestions vec /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us /// an opportunity to look for another type in the chain that will be trivially copyable. @@ -229,25 +169,15 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { // but let's avoid any chance of an ICE if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) { if ty.is_trivially_pure_clone_copy() { - self.current_sig_drop.replace(FoundSigDrop { - found_span: expr.span, - is_unit_return_val: false, - lint_suggestion: LintSuggestion::MoveAndDerefToCopy, - }); + self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndDerefToCopy); } else if allow_move_and_clone { - self.current_sig_drop.replace(FoundSigDrop { - found_span: expr.span, - is_unit_return_val: false, - lint_suggestion: LintSuggestion::MoveAndClone, - }); + self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone); } } } else if ty.is_trivially_pure_clone_copy() { - self.current_sig_drop.replace(FoundSigDrop { - found_span: expr.span, - is_unit_return_val: false, - lint_suggestion: LintSuggestion::MoveOnly, - }); + self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveOnly); + } else if allow_move_and_clone { + self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone); } } @@ -279,11 +209,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { // If either side had a significant drop, suggest moving the entire scrutinee to avoid // unnecessary copies and to simplify cases where both sides have significant drops. if self.has_significant_drop { - self.current_sig_drop.replace(FoundSigDrop { - found_span: span, - is_unit_return_val, - lint_suggestion: LintSuggestion::MoveOnly, - }); + self.replace_current_sig_drop(span, is_unit_return_val, LintSuggestion::MoveOnly); } self.special_handling_for_binary_op = false; @@ -363,34 +289,34 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { } } ExprKind::Box(..) | - ExprKind::Array(..) | - ExprKind::Call(..) | - ExprKind::Unary(..) | - ExprKind::If(..) | - ExprKind::Match(..) | - ExprKind::Field(..) | - ExprKind::Index(..) | - ExprKind::Ret(..) | - ExprKind::Repeat(..) | - ExprKind::Yield(..) | - ExprKind::MethodCall(..) => walk_expr(self, ex), + ExprKind::Array(..) | + ExprKind::Call(..) | + ExprKind::Unary(..) | + ExprKind::If(..) | + ExprKind::Match(..) | + ExprKind::Field(..) | + ExprKind::Index(..) | + ExprKind::Ret(..) | + ExprKind::Repeat(..) | + ExprKind::Yield(..) | + ExprKind::MethodCall(..) => walk_expr(self, ex), ExprKind::AddrOf(_, _, _) | - ExprKind::Block(_, _) | - ExprKind::Break(_, _) | - ExprKind::Cast(_, _) | - // Don't want to check the closure itself, only invocation, which is covered by MethodCall - ExprKind::Closure(_, _, _, _, _) | - ExprKind::ConstBlock(_) | - ExprKind::Continue(_) | - ExprKind::DropTemps(_) | - ExprKind::Err | - ExprKind::InlineAsm(_) | - ExprKind::Let(_) | - ExprKind::Lit(_) | - ExprKind::Loop(_, _, _, _) | - ExprKind::Path(_) | - ExprKind::Struct(_, _, _) | - ExprKind::Type(_, _) => { + ExprKind::Block(_, _) | + ExprKind::Break(_, _) | + ExprKind::Cast(_, _) | + // Don't want to check the closure itself, only invocation, which is covered by MethodCall + ExprKind::Closure(_, _, _, _, _) | + ExprKind::ConstBlock(_) | + ExprKind::Continue(_) | + ExprKind::DropTemps(_) | + ExprKind::Err | + ExprKind::InlineAsm(_) | + ExprKind::Let(_) | + ExprKind::Lit(_) | + ExprKind::Loop(_, _, _, _) | + ExprKind::Path(_) | + ExprKind::Struct(_, _, _) | + ExprKind::Type(_, _) => { return; } } diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs new file mode 100644 index 00000000000..0491a0679f3 --- /dev/null +++ b/clippy_lints/src/matches/try_err.rs @@ -0,0 +1,145 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::LangItem::ResultErr; +use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{hygiene, sym}; + +use super::TRY_ERR; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutinee: &'tcx Expr<'_>) { + // Looks for a structure like this: + // match ::std::ops::Try::into_result(Err(5)) { + // ::std::result::Result::Err(err) => + // #[allow(unreachable_code)] + // return ::std::ops::Try::from_error(::std::convert::From::from(err)), + // ::std::result::Result::Ok(val) => + // #[allow(unreachable_code)] + // val, + // }; + if_chain! { + if let ExprKind::Call(match_fun, try_args) = scrutinee.kind; + if let ExprKind::Path(ref match_fun_path) = match_fun.kind; + if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)); + if let Some(try_arg) = try_args.get(0); + if let ExprKind::Call(err_fun, err_args) = try_arg.kind; + if let Some(err_arg) = err_args.get(0); + if let ExprKind::Path(ref err_fun_path) = err_fun.kind; + if is_lang_ctor(cx, err_fun_path, ResultErr); + if let Some(return_ty) = find_return_type(cx, &expr.kind); + then { + let prefix; + let suffix; + let err_ty; + + if let Some(ty) = result_error_type(cx, return_ty) { + prefix = "Err("; + suffix = ")"; + err_ty = ty; + } else if let Some(ty) = poll_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Err("; + suffix = "))"; + err_ty = ty; + } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Some(Err("; + suffix = ")))"; + err_ty = ty; + } else { + return; + }; + + let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); + let mut applicability = Applicability::MachineApplicable; + let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); + let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { + "" // already returns + } else { + "return " + }; + let suggestion = if err_ty == expr_err_ty { + format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix) + } else { + format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix) + }; + + span_lint_and_sugg( + cx, + TRY_ERR, + expr.span, + "returning an `Err(_)` with the `?` operator", + "try this", + suggestion, + applicability, + ); + } + } +} + +/// Finds function return type by examining return expressions in match arms. +fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { + if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr { + for arm in arms.iter() { + if let ExprKind::Ret(Some(ret)) = arm.body.kind { + return Some(cx.typeck_results().expr_ty(ret)); + } + } + } + None +} + +/// Extracts the error type from Result. +fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(_, subst) = ty.kind(); + if is_type_diagnostic_item(cx, ty, sym::Result); + then { + Some(subst.type_at(1)) + } else { + None + } + } +} + +/// Extracts the error type from Poll>. +fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(def, subst) = ty.kind(); + if match_def_path(cx, def.did(), &paths::POLL); + let ready_ty = subst.type_at(0); + + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); + if cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()); + then { + Some(ready_subst.type_at(1)) + } else { + None + } + } +} + +/// Extracts the error type from Poll>>. +fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(def, subst) = ty.kind(); + if match_def_path(cx, def.did(), &paths::POLL); + let ready_ty = subst.type_at(0); + + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); + if cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()); + let some_ty = ready_subst.type_at(0); + + if let ty::Adt(some_def, some_subst) = some_ty.kind(); + if cx.tcx.is_diagnostic_item(sym::Result, some_def.did()); + then { + Some(some_subst.type_at(1)) + } else { + None + } + } +} diff --git a/clippy_lints/src/methods/get_last_with_len.rs b/clippy_lints/src/methods/get_last_with_len.rs new file mode 100644 index 00000000000..23368238ef5 --- /dev/null +++ b/clippy_lints/src/methods/get_last_with_len.rs @@ -0,0 +1,55 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::SpanlessEq; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::source_map::Spanned; +use rustc_span::sym; + +use super::GET_LAST_WITH_LEN; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { + // Argument to "get" is a subtraction + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + lhs, + rhs, + ) = arg.kind + + // LHS of subtraction is "x.len()" + && let ExprKind::MethodCall(lhs_path, [lhs_recv], _) = &lhs.kind + && lhs_path.ident.name == sym::len + + // RHS of subtraction is 1 + && let ExprKind::Lit(rhs_lit) = &rhs.kind + && let LitKind::Int(1, ..) = rhs_lit.node + + // check that recv == lhs_recv `recv.get(lhs_recv.len() - 1)` + && SpanlessEq::new(cx).eq_expr(recv, lhs_recv) + && !recv.can_have_side_effects() + { + let method = match cx.typeck_results().expr_ty_adjusted(recv).peel_refs().kind() { + ty::Adt(def, _) if cx.tcx.is_diagnostic_item(sym::VecDeque, def.did()) => "back", + ty::Slice(_) => "last", + _ => return, + }; + + let mut applicability = Applicability::MachineApplicable; + let recv_snippet = snippet_with_applicability(cx, recv.span, "_", &mut applicability); + + span_lint_and_sugg( + cx, + GET_LAST_WITH_LEN, + expr.span, + &format!("accessing last element with `{recv_snippet}.get({recv_snippet}.len() - 1)`"), + "try", + format!("{recv_snippet}.{method}()"), + applicability, + ); + } +} diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index d053ff56756..b8d1dabe007 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -34,13 +34,18 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal if let ast::LitKind::Int(start_idx, _) = start_lit.node; then { let mut applicability = Applicability::MachineApplicable; + let suggest = if start_idx == 0 { + format!("{}.first()", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) + } else { + format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx) + }; span_lint_and_sugg( cx, ITER_NEXT_SLICE, expr.span, "using `.iter().next()` on a Slice without end index", "try calling", - format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), + suggest, applicability, ); } @@ -55,7 +60,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal "using `.iter().next()` on an array", "try calling", format!( - "{}.get(0)", + "{}.first()", snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability) ), applicability, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3bf48e18019..7308e74c323 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -21,6 +21,7 @@ mod filter_next; mod flat_map_identity; mod flat_map_option; mod from_iter_instead_of_collect; +mod get_last_with_len; mod get_unwrap; mod implicit_clone; mod inefficient_to_string; @@ -44,6 +45,7 @@ mod map_identity; mod map_unwrap_or; mod needless_option_as_deref; mod needless_option_take; +mod no_effect_replace; mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; @@ -192,25 +194,18 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust - /// # let opt = Some(1); - /// - /// // Bad - /// opt.unwrap(); - /// - /// // Good - /// opt.expect("more helpful message"); + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// option.unwrap(); + /// result.unwrap(); /// ``` /// - /// // or - /// + /// Use instead: /// ```rust - /// # let res: Result = Ok(1); - /// - /// // Bad - /// res.unwrap(); - /// - /// // Good - /// res.expect("more helpful message"); + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// option.expect("more helpful message"); + /// result.expect("more helpful message"); /// ``` #[clippy::version = "1.45.0"] pub UNWRAP_USED, @@ -233,27 +228,21 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust,ignore - /// # let opt = Some(1); - /// - /// // Bad - /// opt.expect("one"); - /// - /// // Good - /// let opt = Some(1); - /// opt?; + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// option.expect("one"); + /// result.expect("one"); /// ``` /// + /// Use instead: + /// ```rust,ignore + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// option?; + /// /// // or /// - /// ```rust - /// # let res: Result = Ok(1); - /// - /// // Bad - /// res.expect("one"); - /// - /// // Good - /// res?; - /// # Ok::<(), ()>(()) + /// result?; /// ``` #[clippy::version = "1.45.0"] pub EXPECT_USED, @@ -429,26 +418,20 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust - /// # let x = Some(1); - /// - /// // Bad - /// x.map(|a| a + 1).unwrap_or(0); - /// - /// // Good - /// x.map_or(0, |a| a + 1); + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// # fn some_function(foo: ()) -> usize { 1 } + /// option.map(|a| a + 1).unwrap_or(0); + /// result.map(|a| a + 1).unwrap_or_else(some_function); /// ``` /// - /// // or - /// + /// Use instead: /// ```rust - /// # let x: Result = Ok(1); + /// # let option = Some(1); + /// # let result: Result = Ok(1); /// # fn some_function(foo: ()) -> usize { 1 } - /// - /// // Bad - /// x.map(|a| a + 1).unwrap_or_else(some_function); - /// - /// // Good - /// x.map_or_else(some_function, |a| a + 1); + /// option.map_or(0, |a| a + 1); + /// result.map_or_else(some_function, |a| a + 1); /// ``` #[clippy::version = "1.45.0"] pub MAP_UNWRAP_OR, @@ -791,13 +774,14 @@ declare_clippy_lint! { /// # let foo = Some(String::new()); /// foo.unwrap_or(String::new()); /// ``` - /// this can instead be written: + /// + /// Use instead: /// ```rust /// # let foo = Some(String::new()); /// foo.unwrap_or_else(String::new); - /// ``` - /// or - /// ```rust + /// + /// // or + /// /// # let foo = Some(String::new()); /// foo.unwrap_or_default(); /// ``` @@ -861,15 +845,14 @@ declare_clippy_lint! { /// # let err_code = "418"; /// # let err_msg = "I'm a teapot"; /// foo.expect(&format!("Err {}: {}", err_code, err_msg)); - /// ``` - /// or - /// ```rust + /// + /// // or + /// /// # let foo = Some(String::new()); - /// # let err_code = "418"; - /// # let err_msg = "I'm a teapot"; /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str()); /// ``` - /// this can instead be written: + /// + /// Use instead: /// ```rust /// # let foo = Some(String::new()); /// # let err_code = "418"; @@ -1209,6 +1192,38 @@ declare_clippy_lint! { "replace `.drain(..)` with `.into_iter()`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for using `x.get(x.len() - 1)` instead of + /// `x.last()`. + /// + /// ### Why is this bad? + /// Using `x.last()` is easier to read and has the same + /// result. + /// + /// Note that using `x[x.len() - 1]` is semantically different from + /// `x.last()`. Indexing into the array will panic on out-of-bounds + /// accesses, while `x.get()` and `x.last()` will return `None`. + /// + /// There is another lint (get_unwrap) that covers the case of using + /// `x.get(index).unwrap()` instead of `x[index]`. + /// + /// ### Example + /// ```rust + /// // Bad + /// let x = vec![2, 3, 5]; + /// let last_element = x.get(x.len() - 1); + /// + /// // Good + /// let x = vec![2, 3, 5]; + /// let last_element = x.last(); + /// ``` + #[clippy::version = "1.37.0"] + pub GET_LAST_WITH_LEN, + complexity, + "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler" +} + declare_clippy_lint! { /// ### What it does /// Checks for use of `.get().unwrap()` (or @@ -2195,6 +2210,24 @@ declare_clippy_lint! { "using `.as_ref().take()` on a temporary value" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `replace` statements which have no effect. + /// + /// ### Why is this bad? + /// It's either a mistake or confusing. + /// + /// ### Example + /// ```rust + /// "1234".replace("12", "12"); + /// "1234".replacen("12", "12", 1); + /// ``` + #[clippy::version = "1.62.0"] + pub NO_EFFECT_REPLACE, + suspicious, + "replace with no effect" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2264,6 +2297,7 @@ impl_lint_pass!(Methods => [ BYTES_NTH, ITER_SKIP_NEXT, GET_UNWRAP, + GET_LAST_WITH_LEN, STRING_EXTEND_CHARS, ITER_CLONED_COLLECT, ITER_WITH_DRAIN, @@ -2294,6 +2328,7 @@ impl_lint_pass!(Methods => [ NEEDLESS_OPTION_AS_DEREF, IS_DIGIT_ASCII_RADIX, NEEDLESS_OPTION_TAKE, + NO_EFFECT_REPLACE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2590,6 +2625,7 @@ impl Methods { inspect_for_each::check(cx, expr, span2); } }, + ("get", [arg]) => get_last_with_len::check(cx, expr, recv, arg), ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), ("is_file", []) => filetype_is_file::check(cx, expr, recv), ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), @@ -2705,6 +2741,9 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); }, }, + ("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => { + no_effect_replace::check(cx, expr, arg1, arg2); + }, _ => {}, } } diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs new file mode 100644 index 00000000000..a76341855b6 --- /dev/null +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -0,0 +1,47 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::SpanlessEq; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_hir::ExprKind; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::NO_EFFECT_REPLACE; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx rustc_hir::Expr<'_>, + arg1: &'tcx rustc_hir::Expr<'_>, + arg2: &'tcx rustc_hir::Expr<'_>, +) { + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + if !(ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)) { + return; + } + + if_chain! { + if let ExprKind::Lit(spanned) = &arg1.kind; + if let Some(param1) = lit_string_value(&spanned.node); + + if let ExprKind::Lit(spanned) = &arg2.kind; + if let LitKind::Str(param2, _) = &spanned.node; + if param1 == param2.as_str(); + + then { + span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); + } + } + + if SpanlessEq::new(cx).eq_expr(arg1, arg2) { + span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); + } +} + +fn lit_string_value(node: &LitKind) -> Option { + match node { + LitKind::Char(value) => Some(value.to_string()), + LitKind::Str(value, _) => Some(value.as_str().to_owned()), + _ => None, + } +} diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 76bc9466ed8..8989db54f6c 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -97,7 +97,7 @@ pub(super) fn check<'tcx>( let func_snippet = snippet(cx, map_arg.span, ".."); let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ `and_then(..)` instead"; - return span_lint_and_sugg( + span_lint_and_sugg( cx, OPTION_MAP_OR_NONE, expr.span, @@ -110,7 +110,7 @@ pub(super) fn check<'tcx>( let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \ `ok()` instead"; let self_snippet = snippet(cx, recv.span, ".."); - return span_lint_and_sugg( + span_lint_and_sugg( cx, RESULT_MAP_OR_INTO_OPTION, expr.span, diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 97c4feb3122..b4c6bfb31ed 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -415,7 +415,7 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - is_clone_like(cx, &*method_name.as_str(), method_def_id) + is_clone_like(cx, method_name.as_str(), method_def_id) || is_cow_into_owned(cx, method_name, method_def_id) || is_to_string(cx, method_name, method_def_id) } diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 65d1f440b76..a081cde8572 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -18,11 +18,11 @@ declare_clippy_lint! { /// the least it hurts readability of the code. /// /// ### Example - /// ```ignore + /// ```rust,ignore /// min(0, max(100, x)) - /// ``` - /// or - /// ```ignore + /// + /// // or + /// /// x.max(100).min(0) /// ``` /// It will always be equal to `0`. Probably the author meant to clamp the value diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 7fdc28c5a06..55665699453 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -103,11 +103,14 @@ declare_clippy_lint! { /// let x = 1.2331f64; /// let y = 1.2332f64; /// - /// // Bad /// if y == 1.23f64 { } /// if y != x {} // where both are floats + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x = 1.2331f64; + /// # let y = 1.2332f64; /// let error_margin = f64::EPSILON; // Use an epsilon for comparison /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. /// // let error_margin = std::f64::EPSILON; @@ -258,10 +261,13 @@ declare_clippy_lint! { /// let x: f64 = 1.0; /// const ONE: f64 = 1.00; /// - /// // Bad /// if x == ONE { } // where both are floats + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x: f64 = 1.0; + /// # const ONE: f64 = 1.00; /// let error_margin = f64::EPSILON; // Use an epsilon for comparison /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. /// // let error_margin = std::f64::EPSILON; diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs new file mode 100644 index 00000000000..d466d54a6ba --- /dev/null +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -0,0 +1,116 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{GenericArg, Item, ItemKind, QPath, Ty, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::GenericParamDefKind; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for type parameters which are positioned inconsistently between + /// a type definition and impl block. Specifically, a paramater in an impl + /// block which has the same name as a parameter in the type def, but is in + /// a different place. + /// + /// ### Why is this bad? + /// Type parameters are determined by their position rather than name. + /// Naming type parameters inconsistently may cause you to refer to the + /// wrong type parameter. + /// + /// ### Example + /// ```rust + /// struct Foo { + /// x: A, + /// y: B, + /// } + /// // inside the impl, B refers to Foo::A + /// impl Foo {} + /// ``` + /// Use instead: + /// ```rust + /// struct Foo { + /// x: A, + /// y: B, + /// } + /// impl Foo {} + /// ``` + #[clippy::version = "1.62.0"] + pub MISMATCHING_TYPE_PARAM_ORDER, + pedantic, + "type parameter positioned inconsistently between type def and impl block" +} +declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]); + +impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if_chain! { + if !item.span.from_expansion(); + if let ItemKind::Impl(imp) = &item.kind; + if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind; + if let Some(segment) = path.segments.iter().next(); + if let Some(generic_args) = segment.args; + if !generic_args.args.is_empty(); + then { + // get the name and span of the generic parameters in the Impl + let impl_params = generic_args.args.iter() + .filter_map(|p| + match p { + GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) => + Some((path.segments[0].ident.to_string(), path.span)), + _ => None, + } + ); + + // find the type that the Impl is for + // only lint on struct/enum/union for now + let defid = match path.res { + Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid, + _ => return, + }; + + // get the names of the generic parameters in the type + let type_params = &cx.tcx.generics_of(defid).params; + let type_param_names: Vec<_> = type_params.iter() + .filter_map(|p| + match p.kind { + GenericParamDefKind::Type {..} => Some(p.name.to_string()), + _ => None, + } + ).collect(); + // hashmap of name -> index for mismatch_param_name + let type_param_names_hashmap: FxHashMap<&String, usize> = + type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect(); + + let type_name = segment.ident; + for (i, (impl_param_name, impl_param_span)) in impl_params.enumerate() { + if mismatch_param_name(i, &impl_param_name, &type_param_names_hashmap) { + let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order", + type_name, impl_param_name); + let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params", + type_param_names[i], type_name); + span_lint_and_help( + cx, + MISMATCHING_TYPE_PARAM_ORDER, + impl_param_span, + &msg, + None, + &help + ); + } + } + } + } + } +} + +// Checks if impl_param_name is the same as one of type_param_names, +// and is in a different position +fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool { + if let Some(j) = type_param_names.get(impl_param_name) { + if i != *j { + return true; + } + } + false +} diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 405fc23e8de..024bd076071 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -120,7 +120,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { self.visit_expr(if_expr); } // make sure top level arm expressions aren't linted - self.maybe_walk_expr(&*arm.body); + self.maybe_walk_expr(arm.body); } }, _ => walk_expr(self, e), diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index cba54e14212..7e2531c7ca5 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { if let hir::PatKind::Wild = local.pat.kind { return; } - check_ty(cx, local.span, cx.typeck_results().pat_ty(&*local.pat)); + check_ty(cx, local.span, cx.typeck_results().pat_ty(local.pat)); } } diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index bb6d820b08c..44fdf84c6df 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -22,9 +22,12 @@ declare_clippy_lint! { /// ### Example /// ```rust,ignore /// debug_assert_eq!(vec![3].pop(), Some(3)); + /// /// // or - /// fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() } - /// debug_assert!(take_a_mut_parameter(&mut 5)); + /// + /// # let mut x = 5; + /// # fn takes_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() } + /// debug_assert!(takes_a_mut_parameter(&mut x)); /// ``` #[clippy::version = "1.40.0"] pub DEBUG_ASSERT_WITH_MUT_CALL, diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index b70871b38be..26c694a71fe 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -194,14 +194,15 @@ fn assignment_suggestions<'tcx>( })) .collect::>>()?; - let applicability = if suggestions.len() > 1 { + match suggestions.len() { + // All of `exprs` are never types + // https://github.com/rust-lang/rust-clippy/issues/8911 + 0 => None, + 1 => Some((Applicability::MachineApplicable, suggestions)), // multiple suggestions don't work with rustfix in multipart_suggest // https://github.com/rust-lang/rustfix/issues/141 - Applicability::Unspecified - } else { - Applicability::MachineApplicable - }; - Some((applicability, suggestions)) + _ => Some((Applicability::Unspecified, suggestions)), + } } struct Usage<'tcx> { diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/numeric_arithmetic.rs similarity index 97% rename from clippy_lints/src/arithmetic.rs rename to clippy_lints/src/numeric_arithmetic.rs index c5948707c81..5c4de338149 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/numeric_arithmetic.rs @@ -51,16 +51,16 @@ declare_clippy_lint! { } #[derive(Copy, Clone, Default)] -pub struct Arithmetic { +pub struct NumericArithmetic { expr_span: Option, /// This field is used to check whether expressions are constants, such as in enum discriminants /// and consts const_span: Option, } -impl_lint_pass!(Arithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]); +impl_lint_pass!(NumericArithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]); -impl<'tcx> LateLintPass<'tcx> for Arithmetic { +impl<'tcx> LateLintPass<'tcx> for NumericArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if self.expr_span.is_some() { return; diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index b7a56970b33..21acf003d92 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -61,7 +61,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir expr_visitor_no_bodies(|expr| { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true }; if matches!( - &*cx.tcx.item_name(macro_call.def_id).as_str(), + cx.tcx.item_name(macro_call.def_id).as_str(), "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne" ) { panics.push(macro_call.span); diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index e3ded716341..5a93431f25a 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -233,7 +233,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind { - self.check_poly_fn(cx, item.def_id, &*method_sig.decl, None); + self.check_poly_fn(cx, item.def_id, method_sig.decl, None); } } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 26dc88a406e..eea036178b8 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -194,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { }, ExprKind::Binary(ref op, l, r) => { if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) { - check_possible_range_contains(cx, op.node, l, r, expr); + check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } }, _ => {}, @@ -213,12 +213,12 @@ fn check_possible_range_contains( left: &Expr<'_>, right: &Expr<'_>, expr: &Expr<'_>, + span: Span, ) { if in_constant(cx, expr.hir_id) { return; } - let span = expr.span; let combine_and = match op { BinOpKind::And | BinOpKind::BitAnd => true, BinOpKind::Or | BinOpKind::BitOr => false, @@ -294,6 +294,20 @@ fn check_possible_range_contains( ); } } + + // If the LHS is the same operator, we have to recurse to get the "real" RHS, since they have + // the same operator precedence + if_chain! { + if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind; + if op == lhs_op.node; + let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()); + if let Some(snip) = &snippet_opt(cx, new_span); + // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong + if snip.matches('(').count() == snip.matches(')').count(); + then { + check_possible_range_contains(cx, op, new_lhs, right, expr, new_span); + } + } } struct RangeBounds<'a> { diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs index 110f58f3734..8db8c4e9b78 100644 --- a/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -2,7 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::last_path_segment; use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::paths; use clippy_utils::source::{indent_of, snippet}; +use clippy_utils::ty::match_type; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -11,10 +13,11 @@ use rustc_span::{sym, Span, Symbol}; declare_clippy_lint! { /// ### What it does - /// Checks for `Arc::new` or `Rc::new` in `vec![elem; len]` + /// Checks for reference-counted pointers (`Arc`, `Rc`, `rc::Weak`, and `sync::Weak`) + /// in `vec![elem; len]` /// /// ### Why is this bad? - /// This will create `elem` once and clone it `len` times - doing so with `Arc` or `Rc` + /// This will create `elem` once and clone it `len` times - doing so with `Arc`/`Rc`/`Weak` /// is a bit misleading, as it will create references to the same pointer, rather /// than different instances. /// @@ -26,7 +29,6 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// /// // Initialize each value separately: /// let mut data = Vec::with_capacity(100); /// for _ in 0..100 { @@ -42,7 +44,7 @@ declare_clippy_lint! { #[clippy::version = "1.62.0"] pub RC_CLONE_IN_VEC_INIT, suspicious, - "initializing `Arc` or `Rc` in `vec![elem; len]`" + "initializing reference-counted pointer in `vec![elem; len]`" } declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]); @@ -50,26 +52,12 @@ impl LateLintPass<'_> for RcCloneInVecInit { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; }; - let Some(symbol) = new_reference_call(cx, elem) else { return; }; + let Some((symbol, func_span)) = ref_init(cx, elem) else { return; }; - emit_lint(cx, symbol, macro_call.span, elem, len); + emit_lint(cx, symbol, macro_call.span, elem, len, func_span); } } -fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String { - let elem_snippet = snippet(cx, elem.span, "..").to_string(); - if elem_snippet.contains('\n') { - // This string must be found in `elem_snippet`, otherwise we won't be constructing - // the snippet in the first place. - let reference_creation = format!("{symbol_name}::new"); - let (code_until_reference_creation, _right) = elem_snippet.split_once(&reference_creation).unwrap(); - - return format!("{code_until_reference_creation}{reference_creation}(..)"); - } - - elem_snippet -} - fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String { format!( r#"{{ @@ -89,17 +77,17 @@ fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String { ) } -fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>) { +fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>, func_span: Span) { let symbol_name = symbol.as_str(); span_lint_and_then( cx, RC_CLONE_IN_VEC_INIT, lint_span, - &format!("calling `{symbol_name}::new` in `vec![elem; len]`"), + "initializing a reference-counted pointer in `vec![elem; len]`", |diag| { let len_snippet = snippet(cx, len.span, ".."); - let elem_snippet = elem_snippet(cx, elem, symbol_name); + let elem_snippet = format!("{}(..)", snippet(cx, elem.span.with_hi(func_span.hi()), "..")); let indentation = " ".repeat(indent_of(cx, lint_span).unwrap_or(0)); let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); @@ -109,7 +97,7 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr< lint_span, format!("consider initializing each `{symbol_name}` element individually"), loop_init_suggestion, - Applicability::Unspecified, + Applicability::HasPlaceholders, ); diag.span_suggestion( lint_span, @@ -117,23 +105,33 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr< "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" ), extract_suggestion, - Applicability::Unspecified, + Applicability::HasPlaceholders, ); }, ); } -/// Checks whether the given `expr` is a call to `Arc::new` or `Rc::new` -fn new_reference_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +/// Checks whether the given `expr` is a call to `Arc::new`, `Rc::new`, or evaluates to a `Weak` +fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, Span)> { if_chain! { if let ExprKind::Call(func, _args) = expr.kind; if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind; if let TyKind::Path(ref ty_path) = ty.kind; if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id(); - if last_path_segment(func_path).ident.name == sym::new; then { - return cx.tcx.get_diagnostic_name(def_id).filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc); + if last_path_segment(func_path).ident.name == sym::new + && let Some(symbol) = cx + .tcx + .get_diagnostic_name(def_id) + .filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc) { + return Some((symbol, func.span)); + } + + let ty_path = cx.typeck_results().expr_ty(expr); + if match_type(cx, ty_path, &paths::WEAK_RC) || match_type(cx, ty_path, &paths::WEAK_ARC) { + return Some((Symbol::intern("Weak"), func.span)); + } } } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index ab16fe47d4d..249f11f9850 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -288,8 +288,8 @@ fn is_call_with_ref_arg<'tcx>( if let mir::TerminatorKind::Call { func, args, destination, .. } = kind; if args.len() == 1; if let mir::Operand::Move(mir::Place { local, .. }) = &args[0]; - if let ty::FnDef(def_id, _) = *func.ty(&*mir, cx.tcx).kind(); - if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx)); + if let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind(); + if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(mir, cx.tcx)); if !is_copy(cx, inner_ty); then { Some((def_id, *local, inner_ty, destination.as_local()?)) @@ -318,7 +318,7 @@ fn find_stmt_assigns_to<'tcx>( None })?; - match (by_ref, &*rvalue) { + match (by_ref, rvalue) { (true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => { Some(base_local_and_movability(cx, mir, *place)) }, diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 2d26c49252f..0825f00f421 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -51,12 +51,12 @@ impl RedundantStaticLifetimes { fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) { match ty.kind { // Be careful of nested structures (arrays and tuples) - TyKind::Array(ref ty, _) => { - self.visit_type(&*ty, cx, reason); + TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => { + self.visit_type(ty, cx, reason); }, TyKind::Tup(ref tup) => { for tup_ty in tup { - self.visit_type(&*tup_ty, cx, reason); + self.visit_type(tup_ty, cx, reason); } }, // This is what we are looking for ! @@ -89,9 +89,6 @@ impl RedundantStaticLifetimes { } self.visit_type(&*borrow_type.ty, cx, reason); }, - TyKind::Slice(ref ty) => { - self.visit_type(ty, cx, reason); - }, _ => {}, } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 8068fa22d9c..e525eba53e2 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::{fn_def_id, path_to_local_id}; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -226,14 +226,10 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option { - if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { - return; - } - + let mut applicability = Applicability::MachineApplicable; span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); - } + let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability); + diag.span_suggestion(ret_span, "remove `return`", snippet, applicability); }); }, None => match replacement { @@ -287,7 +283,7 @@ struct BorrowVisitor<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows { + if self.borrows || expr.span.from_expansion() { return; } diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 2a80e6f918d..4f74c1e44c2 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -5,9 +5,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::hir_id::ItemLocalId; -use rustc_hir::{ - Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp, -}; +use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, Symbol}; @@ -141,31 +139,34 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { let hir = cx.tcx.hir(); - if !matches!(hir.body_owner_kind(hir.body_owner_def_id(body.id())), BodyOwnerKind::Closure) - { + if !matches!( + hir.body_owner_kind(hir.body_owner_def_id(body.id())), + BodyOwnerKind::Closure + ) { self.bindings.push(FxHashMap::default()); } } fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { let hir = cx.tcx.hir(); - if !matches!(hir.body_owner_kind(hir.body_owner_def_id(body.id())), BodyOwnerKind::Closure) - { + if !matches!( + hir.body_owner_kind(hir.body_owner_def_id(body.id())), + BodyOwnerKind::Closure + ) { self.bindings.pop(); } } } -fn is_shadow( - cx: &LateContext<'_>, - owner: LocalDefId, - first: ItemLocalId, - second: ItemLocalId, -) -> bool { - let scope_tree = cx.tcx.region_scope_tree(owner); - let first_scope = scope_tree.var_scope(first).unwrap(); - let second_scope = scope_tree.var_scope(second).unwrap(); - scope_tree.is_subscope_of(second_scope, first_scope) +fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool { + let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id()); + if let Some(first_scope) = scope_tree.var_scope(first) { + if let Some(second_scope) = scope_tree.var_scope(second) { + return scope_tree.is_subscope_of(second_scope, first_scope); + } + } + + false } fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) { @@ -177,16 +178,15 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) snippet(cx, expr.span, "..") ); (SHADOW_SAME, msg) - } + }, Some(expr) if is_local_used(cx, expr, shadowed) => { let msg = format!("`{}` is shadowed", snippet(cx, pat.span, "_")); (SHADOW_REUSE, msg) - } + }, _ => { - let msg = - format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_")); + let msg = format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_")); (SHADOW_UNRELATED, msg) - } + }, }; span_lint_and_note( cx, @@ -215,7 +215,14 @@ fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_ expr = match expr.kind { ExprKind::Box(e) | ExprKind::AddrOf(_, _, e) - | ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _) + | ExprKind::Block( + &Block { + stmts: [], + expr: Some(e), + .. + }, + _, + ) | ExprKind::Unary(UnOp::Deref, e) => e, ExprKind::Path(QPath::Resolved(None, path)) => break path.res == Res::Local(hir_id), _ => break false, diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 3d7dc49b406..bfb9f0d01e1 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -110,7 +110,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods if let ExprKind::MethodCall(method_path, [ptr_self, .., count], _) = expr.kind; let method_ident = method_path.ident.as_str(); - if METHODS.iter().any(|m| *m == &*method_ident); + if METHODS.iter().any(|m| *m == method_ident); // Get the pointee type if let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) = diff --git a/clippy_lints/src/swap_ptr_to_ref.rs b/clippy_lints/src/swap_ptr_to_ref.rs new file mode 100644 index 00000000000..75d3b040c96 --- /dev/null +++ b/clippy_lints/src/swap_ptr_to_ref.rs @@ -0,0 +1,80 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_context; +use clippy_utils::{match_def_path, path_def_id, paths}; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{Span, SyntaxContext}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `core::mem::swap` where either parameter is derived from a pointer + /// + /// ### Why is this bad? + /// When at least one parameter to `swap` is derived from a pointer it may overlap with the + /// other. This would then lead to undefined behavior. + /// + /// ### Example + /// ```rust + /// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) { + /// for (&x, &y) in x.iter().zip(y) { + /// core::mem::swap(&mut *x, &mut *y); + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) { + /// for (&x, &y) in x.iter().zip(y) { + /// core::ptr::swap(x, y); + /// } + /// } + /// ``` + #[clippy::version = "1.63.0"] + pub SWAP_PTR_TO_REF, + suspicious, + "call to `mem::swap` using pointer derived references" +} +declare_lint_pass!(SwapPtrToRef => [SWAP_PTR_TO_REF]); + +impl LateLintPass<'_> for SwapPtrToRef { + fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { + if let ExprKind::Call(fn_expr, [arg1, arg2]) = e.kind + && let Some(fn_id) = path_def_id(cx, fn_expr) + && match_def_path(cx, fn_id, &paths::MEM_SWAP) + && let ctxt = e.span.ctxt() + && let (from_ptr1, arg1_span) = is_ptr_to_ref(cx, arg1, ctxt) + && let (from_ptr2, arg2_span) = is_ptr_to_ref(cx, arg2, ctxt) + && (from_ptr1 || from_ptr2) + { + span_lint_and_then( + cx, + SWAP_PTR_TO_REF, + e.span, + "call to `core::mem::swap` with a parameter derived from a raw pointer", + |diag| { + if !((from_ptr1 && arg1_span.is_none()) || (from_ptr2 && arg2_span.is_none())) { + let mut app = Applicability::MachineApplicable; + let snip1 = snippet_with_context(cx, arg1_span.unwrap_or(arg1.span), ctxt, "..", &mut app).0; + let snip2 = snippet_with_context(cx, arg2_span.unwrap_or(arg2.span), ctxt, "..", &mut app).0; + diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({}, {})", snip1, snip2), app); + } + } + ); + } + } +} + +/// Checks if the expression converts a mutable pointer to a mutable reference. If it is, also +/// returns the span of the pointer expression if it's suitable for making a suggestion. +fn is_ptr_to_ref(cx: &LateContext<'_>, e: &Expr<'_>, ctxt: SyntaxContext) -> (bool, Option) { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, borrowed_expr) = e.kind + && let ExprKind::Unary(UnOp::Deref, derefed_expr) = borrowed_expr.kind + && cx.typeck_results().expr_ty(derefed_expr).is_unsafe_ptr() + { + (true, (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then(|| derefed_expr.span)) + } else { + (false, None) + } +} diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 911da3997ae..1e5b646f5f0 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -54,14 +54,14 @@ declare_clippy_lint! { /// fn func(arg: T) where T: Clone + Default {} /// ``` /// - /// Could be written as: - /// + /// Use instead: /// ```rust + /// # mod hidden { /// fn func(arg: T) {} - /// ``` - /// or + /// # } + /// + /// // or /// - /// ```rust /// fn func(arg: T) where T: Clone + Default {} /// ``` #[clippy::version = "1.47.0"] diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index d2a040beb0c..cbe1406728b 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -59,7 +59,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub USELESS_TRANSMUTE, - nursery, + complexity, "transmutes that have the same to and from types or could be a cast/coercion" } diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index 3cc3d40a143..a0d104e2390 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -4,7 +4,7 @@ use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TypeFoldable}; /// Checks for `useless_transmute` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -16,7 +16,7 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { - _ if from_ty == to_ty => { + _ if from_ty == to_ty && !from_ty.has_erased_regions() => { span_lint( cx, USELESS_TRANSMUTE, @@ -26,28 +26,31 @@ pub(super) fn check<'tcx>( true }, (ty::Ref(_, rty, rty_mutbl), ty::RawPtr(ptr_ty)) => { - span_lint_and_then( - cx, - USELESS_TRANSMUTE, - e.span, - "transmute from a reference to a pointer", - |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let rty_and_mut = ty::TypeAndMut { - ty: *rty, - mutbl: *rty_mutbl, - }; + // No way to give the correct suggestion here. Avoid linting for now. + if !rty.has_erased_regions() { + span_lint_and_then( + cx, + USELESS_TRANSMUTE, + e.span, + "transmute from a reference to a pointer", + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { + let rty_and_mut = ty::TypeAndMut { + ty: *rty, + mutbl: *rty_mutbl, + }; - let sugg = if *ptr_ty == rty_and_mut { - arg.as_ty(to_ty) - } else { - arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty) - }; + let sugg = if *ptr_ty == rty_and_mut { + arg.as_ty(to_ty) + } else { + arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty) + }; - diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); - } - }, - ); + diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); + } + }, + ); + } true }, (ty::Int(_) | ty::Uint(_), ty::RawPtr(_)) => { diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs deleted file mode 100644 index e108f7be12e..00000000000 --- a/clippy_lints/src/try_err.rs +++ /dev/null @@ -1,186 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::LangItem::ResultErr; -use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{hygiene, sym}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usages of `Err(x)?`. - /// - /// ### Why is this bad? - /// The `?` operator is designed to allow calls that - /// can fail to be easily chained. For example, `foo()?.bar()` or - /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will - /// always return), it is more clear to write `return Err(x)`. - /// - /// ### Example - /// ```rust - /// fn foo(fail: bool) -> Result { - /// if fail { - /// Err("failed")?; - /// } - /// Ok(0) - /// } - /// ``` - /// Could be written: - /// - /// ```rust - /// fn foo(fail: bool) -> Result { - /// if fail { - /// return Err("failed".into()); - /// } - /// Ok(0) - /// } - /// ``` - #[clippy::version = "1.38.0"] - pub TRY_ERR, - restriction, - "return errors explicitly rather than hiding them behind a `?`" -} - -declare_lint_pass!(TryErr => [TRY_ERR]); - -impl<'tcx> LateLintPass<'tcx> for TryErr { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - // Looks for a structure like this: - // match ::std::ops::Try::into_result(Err(5)) { - // ::std::result::Result::Err(err) => - // #[allow(unreachable_code)] - // return ::std::ops::Try::from_error(::std::convert::From::from(err)), - // ::std::result::Result::Ok(val) => - // #[allow(unreachable_code)] - // val, - // }; - if_chain! { - if !in_external_macro(cx.tcx.sess, expr.span); - if let ExprKind::Match(match_arg, _, MatchSource::TryDesugar) = expr.kind; - if let ExprKind::Call(match_fun, try_args) = match_arg.kind; - if let ExprKind::Path(ref match_fun_path) = match_fun.kind; - if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)); - if let Some(try_arg) = try_args.get(0); - if let ExprKind::Call(err_fun, err_args) = try_arg.kind; - if let Some(err_arg) = err_args.get(0); - if let ExprKind::Path(ref err_fun_path) = err_fun.kind; - if is_lang_ctor(cx, err_fun_path, ResultErr); - if let Some(return_ty) = find_return_type(cx, &expr.kind); - then { - let prefix; - let suffix; - let err_ty; - - if let Some(ty) = result_error_type(cx, return_ty) { - prefix = "Err("; - suffix = ")"; - err_ty = ty; - } else if let Some(ty) = poll_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Err("; - suffix = "))"; - err_ty = ty; - } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Some(Err("; - suffix = ")))"; - err_ty = ty; - } else { - return; - }; - - let expr_err_ty = cx.typeck_results().expr_ty(err_arg); - let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); - let mut applicability = Applicability::MachineApplicable; - let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); - let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { - "" // already returns - } else { - "return " - }; - let suggestion = if err_ty == expr_err_ty { - format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix) - } else { - format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix) - }; - - span_lint_and_sugg( - cx, - TRY_ERR, - expr.span, - "returning an `Err(_)` with the `?` operator", - "try this", - suggestion, - applicability, - ); - } - } - } -} - -/// Finds function return type by examining return expressions in match arms. -fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { - if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr { - for arm in arms.iter() { - if let ExprKind::Ret(Some(ret)) = arm.body.kind { - return Some(cx.typeck_results().expr_ty(ret)); - } - } - } - None -} - -/// Extracts the error type from Result. -fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if_chain! { - if let ty::Adt(_, subst) = ty.kind(); - if is_type_diagnostic_item(cx, ty, sym::Result); - then { - Some(subst.type_at(1)) - } else { - None - } - } -} - -/// Extracts the error type from Poll>. -fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if_chain! { - if let ty::Adt(def, subst) = ty.kind(); - if match_def_path(cx, def.did(), &paths::POLL); - let ready_ty = subst.type_at(0); - - if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()); - then { - Some(ready_subst.type_at(1)) - } else { - None - } - } -} - -/// Extracts the error type from Poll>>. -fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if_chain! { - if let ty::Adt(def, subst) = ty.kind(); - if match_def_path(cx, def.did(), &paths::POLL); - let ready_ty = subst.type_at(0); - - if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()); - let some_ty = ready_subst.type_at(0); - - if let ty::Adt(some_def, some_subst) = some_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Result, some_def.did()); - then { - Some(some_subst.type_at(1)) - } else { - None - } - } -} diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs new file mode 100644 index 00000000000..306afe44148 --- /dev/null +++ b/clippy_lints/src/unused_rounding.rs @@ -0,0 +1,69 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// + /// Detects cases where a whole-number literal float is being rounded, using + /// the `floor`, `ceil`, or `round` methods. + /// + /// ### Why is this bad? + /// + /// This is unnecessary and confusing to the reader. Doing this is probably a mistake. + /// + /// ### Example + /// ```rust + /// let x = 1f32.ceil(); + /// ``` + /// Use instead: + /// ```rust + /// let x = 1f32; + /// ``` + #[clippy::version = "1.62.0"] + pub UNUSED_ROUNDING, + nursery, + "Uselessly rounding a whole number floating-point literal" +} +declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); + +fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { + if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind + && let method_name = name_ident.ident.name.as_str() + && (method_name == "ceil" || method_name == "round" || method_name == "floor") + && !args.is_empty() + && let ExprKind::Lit(spanned) = &args[0].kind + && let LitKind::Float(symbol, ty) = spanned.kind { + let f = symbol.as_str().parse::().unwrap(); + let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { + ty.name_str() + } else { + "" + }; + if f.fract() == 0.0 { + Some((method_name, f_str)) + } else { + None + } + } else { + None + } +} + +impl EarlyLintPass for UnusedRounding { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let Some((method_name, float)) = is_useless_rounding(expr) { + span_lint_and_sugg( + cx, + UNUSED_ROUNDING, + expr.span, + &format!("used the `{}` method with a whole number float", method_name), + &format!("remove the `{}` method call", method_name), + float, + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 66f7748e9e0..486ea5e5ccf 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -258,13 +258,21 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if !pat.span.from_expansion(); if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); - if let PatKind::Path(QPath::Resolved(_, path)) = pat.kind; - if !matches!(path.res, Res::SelfTy { .. } | Res::Def(DefKind::TyParam, _)); + // get the path from the pattern + if let PatKind::Path(QPath::Resolved(_, path)) + | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) + | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind; if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id); - if let [first, ..] = path.segments; - if let Some(hir_id) = first.hir_id; then { - span_lint(cx, cx.tcx.hir().span(hir_id)); + match path.res { + Res::Def(DefKind::Ctor(ctor_of, _), ..) => match ctor_of { + CtorOf::Variant => lint_path_to_variant(cx, path), + CtorOf::Struct => span_lint(cx, path.span), + }, + Res::Def(DefKind::Variant, ..) => lint_path_to_variant(cx, path), + Res::Def(DefKind::Struct, ..) => span_lint(cx, path.span), + _ => () + } } } } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index cd4d16fe95f..b5c5d35135f 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -340,6 +340,10 @@ define_Conf! { /// /// Whether `unwrap` should be allowed in test functions (allow_unwrap_in_tests: bool = false), + /// Lint: DBG_MACRO. + /// + /// Whether `dbg!` should be allowed in test functions + (allow_dbg_in_tests: bool = false), } /// Search for the configuration file. diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 0e8f40e9210..60f98876994 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -422,7 +422,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { } } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { if !matches!( - &*cx.tcx.item_name(macro_call.def_id).as_str(), + cx.tcx.item_name(macro_call.def_id).as_str(), "impl_lint_pass" | "declare_lint_pass" ) { return; @@ -504,7 +504,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<' return; } - if RustcVersion::parse(&*value.as_str()).is_err() { + if RustcVersion::parse(value.as_str()).is_err() { span_lint_and_help( cx, INVALID_CLIPPY_VERSION_ATTRIBUTE, @@ -595,7 +595,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { if_chain! { if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind; let fn_name = path.ident; - if let Some(sugg) = self.map.get(&*fn_name.as_str()); + if let Some(sugg) = self.map.get(fn_name.as_str()); let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); @@ -679,7 +679,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { then { let and_then_snippets = get_and_then_snippets(cx, and_then_args); let mut sle = SpanlessEq::new(cx).deny_side_effects(); - match &*ps.ident.as_str() { + match ps.ident.as_str() { "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); }, diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 8c1910b3b2a..cf2de6a42af 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -7,6 +7,7 @@ //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such //! a simple mistake) +use crate::renamed_lints::RENAMED_LINTS; use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type}; use clippy_utils::diagnostics::span_lint; @@ -26,6 +27,7 @@ use rustc_span::{sym, Loc, Span, Symbol}; use serde::{ser::SerializeStruct, Serialize, Serializer}; use std::collections::BinaryHeap; use std::fmt; +use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::path::Path; @@ -85,6 +87,21 @@ macro_rules! CONFIGURATION_VALUE_TEMPLATE { }; } +macro_rules! RENAMES_SECTION_TEMPLATE { + () => { + r#" +### Past names + +{names} +"# + }; +} +macro_rules! RENAME_VALUE_TEMPLATE { + () => { + "* `{name}`\n" + }; +} + const LINT_EMISSION_FUNCTIONS: [&[&str]; 8] = [ &["clippy_utils", "diagnostics", "span_lint"], &["clippy_utils", "diagnostics", "span_lint_and_help"], @@ -198,9 +215,10 @@ impl Drop for MetadataCollector { // Mapping the final data let mut lints = std::mem::take(&mut self.lints).into_sorted_vec(); - lints - .iter_mut() - .for_each(|x| x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default())); + collect_renames(&mut lints); + for x in &mut lints { + x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default()); + } // Outputting if Path::new(OUTPUT_FILE).exists() { @@ -527,12 +545,11 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option, item: &Item<'_>) -> Option { let attrs = cx.tcx.hir().attrs(item.hir_id()); let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str); - let mut docs = String::from(&*lines.next()?.as_str()); + let mut docs = String::from(lines.next()?.as_str()); let mut in_code_block = false; let mut is_code_block_rust = false; for line in lines { let line = line.as_str(); - let line = &*line; // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :) if is_code_block_rust && line.trim_start().starts_with("# ") { @@ -643,6 +660,37 @@ fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { false } +fn collect_renames(lints: &mut Vec) { + for lint in lints { + let mut collected = String::new(); + let mut names = vec![lint.id.clone()]; + + loop { + if let Some(lint_name) = names.pop() { + for (k, v) in RENAMED_LINTS { + if_chain! { + if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); + if name == lint_name; + if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); + then { + write!(collected, RENAME_VALUE_TEMPLATE!(), name = past_name).unwrap(); + names.push(past_name.to_string()); + } + } + } + + continue; + } + + break; + } + + if !collected.is_empty() { + write!(&mut lint.docs, RENAMES_SECTION_TEMPLATE!(), names = collected).unwrap(); + } + } +} + // ================================================================== // Lint emission // ================================================================== diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 9f162a117b2..d487868cafe 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -478,7 +478,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option { if let Some(Constant::Bool(b)) = self.expr(cond) { if b { - self.expr(&*then) + self.expr(then) } else { otherwise.as_ref().and_then(|expr| self.expr(expr)) } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 1e0fc789af2..4604ae5c2c7 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -35,7 +35,7 @@ impl<'tcx> ForLoop<'tcx> { if let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind; if let hir::ExprKind::Call(_, [arg]) = iterexpr.kind; if let hir::ExprKind::Loop(block, ..) = arm.body.kind; - if let [stmt] = &*block.stmts; + if let [stmt] = block.stmts; if let hir::StmtKind::Expr(e) = stmt.kind; if let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind; if let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index fc1a4e1f602..0603471c343 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -411,10 +411,10 @@ impl HirEqInterExpr<'_, '_, '_> { (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl), (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { - l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty) + l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty) }, (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => { - l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty) + l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty) }, (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r), (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), @@ -608,7 +608,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_name(i.ident.name); } if let Some(j) = *j { - self.hash_expr(&*j); + self.hash_expr(j); } }, ExprKind::Box(e) | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 833f8cde63a..5f051e3f444 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1443,7 +1443,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, PatKind::Tuple(pats, _) => are_refutable(cx, pats), PatKind::Struct(ref qpath, fields, _) => { - is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat)) + is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat)) }, PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats), PatKind::Slice(head, middle, tail) => { @@ -1658,7 +1658,7 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, let mut blocks: Vec<&Block<'_>> = Vec::new(); while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) { - conds.push(&*cond); + conds.push(cond); if let ExprKind::Block(block, _) = then.kind { blocks.push(block); } else { @@ -1916,7 +1916,17 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { .. }, .., - ) => cx.typeck_results().qpath_res(qpath, *path_hir_id).opt_def_id(), + ) => { + // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or + // deref to fn pointers, dyn Fn, impl Fn - #8850 + if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) = + cx.typeck_results().qpath_res(qpath, *path_hir_id) + { + Some(id) + } else { + None + } + }, _ => None, } } @@ -2073,7 +2083,8 @@ static TEST_ITEM_NAMES_CACHE: SyncOnceCell(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default())); let mut map: MutexGuard<'_, FxHashMap>> = cache.lock().unwrap(); - match map.entry(module) { + let value = map.entry(module); + match value { Entry::Occupied(entry) => f(entry.get()), Entry::Vacant(entry) => { let mut names = Vec::new(); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 134fd1ce505..b9ec2c19fdd 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -30,6 +30,7 @@ msrv_aliases! { 1,34,0 { TRY_FROM } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,28,0 { FROM_BOOL } + 1,26,0 { RANGE_INCLUSIVE } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } 1,24,0 { IS_ASCII_DIGIT } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 9b9cbff2d14..89789c3d851 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -73,6 +73,7 @@ pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; #[cfg(feature = "internal")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; +pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"]; pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; /// Preferably use the diagnostic item `sym::Option` where possible @@ -141,6 +142,7 @@ pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"]; +pub const SLICE_GET: [&str; 4] = ["core", "slice", "", "get"]; pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec"]; pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 58abef38ea8..498dcbb8900 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -121,24 +121,18 @@ fn check_rvalue<'tcx>( ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), - Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body), Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { check_place(tcx, *place, span, body) }, - Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => { - Err((span, "casting pointers to ints is unstable in const fn".into())) - }, - Rvalue::Cast(CastKind::Misc, operand, _) => { - check_operand(tcx, operand, span, body) - }, - Rvalue::Cast( + Rvalue::Repeat(operand, _) + | Rvalue::Use(operand) + | Rvalue::Cast( CastKind::PointerFromExposedAddress + | CastKind::Misc | CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, - _ - ) => { - check_operand(tcx, operand, span, body) - }, + _, + ) => check_operand(tcx, operand, span, body), Rvalue::Cast( CastKind::Pointer( PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer, @@ -163,6 +157,9 @@ fn check_rvalue<'tcx>( Err((span, "unsizing casts are not allowed in const fn".into())) } }, + Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => { + Err((span, "casting pointers to ints is unstable in const fn".into())) + }, // binops are fine on integers Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => { check_operand(tcx, lhs, span, body)?; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 04ef2f57447..f88a92fb11c 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::hygiene; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Pos, Span, SyntaxContext}; +use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext}; use std::borrow::Cow; /// Checks if the span starts with the given text. This will return false if the span crosses @@ -389,6 +389,27 @@ pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> { without } +/// Trims the whitespace from the start and the end of the span. +pub fn trim_span(sm: &SourceMap, span: Span) -> Span { + let data = span.data(); + let sf: &_ = &sm.lookup_source_file(data.lo); + let Some(src) = sf.src.as_deref() else { + return span; + }; + let Some(snip) = &src.get((data.lo - sf.start_pos).to_usize()..(data.hi - sf.start_pos).to_usize()) else { + return span; + }; + let trim_start = snip.len() - snip.trim_start().len(); + let trim_end = snip.len() - snip.trim_end().len(); + SpanData { + lo: data.lo + BytePos::from_usize(trim_start), + hi: data.hi - BytePos::from_usize(trim_end), + ctxt: data.ctxt, + parent: data.parent, + } + .span() +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 75d27d3b594..a10515d2fec 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,8 @@ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{ - self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr, + self, AdtDef, Binder, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, + VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; @@ -151,18 +152,29 @@ pub fn implements_trait<'tcx>( ty: Ty<'tcx>, trait_id: DefId, ty_params: &[GenericArg<'tcx>], +) -> bool { + implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params) +} + +/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. +pub fn implements_trait_with_env<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ty: Ty<'tcx>, + trait_id: DefId, + ty_params: &[GenericArg<'tcx>], ) -> bool { // Clippy shouldn't have infer types assert!(!ty.needs_infer()); - let ty = cx.tcx.erase_regions(ty); + let ty = tcx.erase_regions(ty); if ty.has_escaping_bound_vars() { return false; } - let ty_params = cx.tcx.mk_substs(ty_params.iter()); - cx.tcx.infer_ctxt().enter(|infcx| { + let ty_params = tcx.mk_substs(ty_params.iter()); + tcx.infer_ctxt().enter(|infcx| { infcx - .type_implements_trait(trait_id, ty, ty_params, cx.param_env) + .type_implements_trait(trait_id, ty, ty_params, param_env) .must_apply_modulo_regions() }) } diff --git a/doc/adding_lints.md b/doc/adding_lints.md index e8f0c338fd5..3e0b1c5c4f7 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -516,11 +516,12 @@ declare_clippy_lint! { /// ### Example /// /// ```rust,ignore - /// // Bad - /// Insert a short example of code that triggers the lint - /// - /// // Good - /// Insert a short example of improved code that doesn't trigger the lint + /// // A short example of code that triggers the lint + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// // A short example of improved code that doesn't trigger the lint /// ``` #[clippy::version = "1.29.0"] pub FOO_FUNCTIONS, diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 131ac3c3611..1d1aee0da2c 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -159,7 +159,8 @@ A list of defined paths for Clippy can be found in [paths.rs][paths] To check if our type defines a method called `some_method`: ```rust -use clippy_utils::{is_type_diagnostic_item, return_ty}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::return_ty; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index e63f65ce2f7..504d58b5197 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -11,7 +11,7 @@ publish = false [dependencies] cargo_metadata = "0.14" -clap = "2.33" +clap = "3.1" flate2 = "1.0" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index de32b484360..a6f93d2a1c0 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -1,47 +1,47 @@ -use clap::{App, Arg, ArgMatches}; +use clap::{Arg, ArgMatches, Command}; use std::env; use std::path::PathBuf; -fn get_clap_config<'a>() -> ArgMatches<'a> { - App::new("lintcheck") +fn get_clap_config() -> ArgMatches { + Command::new("lintcheck") .about("run clippy on a set of crates and check output") .arg( - Arg::with_name("only") + Arg::new("only") .takes_value(true) .value_name("CRATE") .long("only") .help("Only process a single crate of the list"), ) .arg( - Arg::with_name("crates-toml") + Arg::new("crates-toml") .takes_value(true) .value_name("CRATES-SOURCES-TOML-PATH") .long("crates-toml") .help("Set the path for a crates.toml where lintcheck should read the sources from"), ) .arg( - Arg::with_name("threads") + Arg::new("threads") .takes_value(true) .value_name("N") - .short("j") + .short('j') .long("jobs") .help("Number of threads to use, 0 automatic choice"), ) .arg( - Arg::with_name("fix") + Arg::new("fix") .long("--fix") .help("Runs cargo clippy --fix and checks if all suggestions apply"), ) .arg( - Arg::with_name("filter") + Arg::new("filter") .long("--filter") .takes_value(true) - .multiple(true) + .multiple_occurrences(true) .value_name("clippy_lint_name") .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"), ) .arg( - Arg::with_name("markdown") + Arg::new("markdown") .long("--markdown") .help("Change the reports table to use markdown links"), ) diff --git a/rust-toolchain b/rust-toolchain index 997e7ba9382..2386a751f04 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-05-19" +channel = "nightly-2022-06-04" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/compile-test.rs b/tests/compile-test.rs index c9710e3db8e..7d219835723 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,5 +1,6 @@ #![feature(test)] // compiletest_rs requires this attribute #![feature(once_cell)] +#![feature(is_sorted)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] @@ -22,6 +23,7 @@ const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); /// All crates used in UI tests are listed here static TEST_DEPENDENCIES: &[&str] = &[ + "clap", "clippy_utils", "derive_new", "futures", @@ -40,6 +42,8 @@ static TEST_DEPENDENCIES: &[&str] = &[ // Test dependencies may need an `extern crate` here to ensure that they show up // in the depinfo file (otherwise cargo thinks they are unused) #[allow(unused_extern_crates)] +extern crate clap; +#[allow(unused_extern_crates)] extern crate clippy_utils; #[allow(unused_extern_crates)] extern crate derive_new; @@ -109,8 +113,9 @@ static EXTERN_FLAGS: SyncLazy = SyncLazy::new(|| { not_found.is_empty(), "dependencies not found in depinfo: {:?}\n\ help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\ - help: Try adding to dev-dependencies in Cargo.toml", - not_found + help: Try adding to dev-dependencies in Cargo.toml\n\ + help: Be sure to also add `extern crate ...;` to tests/compile-test.rs", + not_found, ); crates .into_iter() @@ -162,7 +167,8 @@ fn base_config(test_dir: &str) -> compiletest::Config { } fn run_ui() { - let config = base_config("ui"); + let mut config = base_config("ui"); + config.rustfix_coverage = true; // use tests/clippy.toml let _g = VarGuard::set("CARGO_MANIFEST_DIR", fs::canonicalize("tests").unwrap()); let _threads = VarGuard::set( @@ -175,6 +181,7 @@ fn run_ui() { }), ); compiletest::run_tests(&config); + check_rustfix_coverage(); } fn run_internal_tests() { @@ -337,6 +344,88 @@ fn compile_test() { run_internal_tests(); } +const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[ + "assign_ops2.rs", + "borrow_deref_ref_unfixable.rs", + "cast_size_32bit.rs", + "char_lit_as_u8.rs", + "cmp_owned/without_suggestion.rs", + "dbg_macro.rs", + "deref_addrof_double_trigger.rs", + "doc/unbalanced_ticks.rs", + "eprint_with_newline.rs", + "explicit_counter_loop.rs", + "iter_skip_next_unfixable.rs", + "let_and_return.rs", + "literals.rs", + "map_flatten.rs", + "map_unwrap_or.rs", + "match_bool.rs", + "mem_replace_macro.rs", + "needless_arbitrary_self_type_unfixable.rs", + "needless_borrow_pat.rs", + "needless_for_each_unfixable.rs", + "nonminimal_bool.rs", + "print_literal.rs", + "print_with_newline.rs", + "redundant_static_lifetimes_multiple.rs", + "ref_binding_to_reference.rs", + "repl_uninit.rs", + "result_map_unit_fn_unfixable.rs", + "search_is_some.rs", + "single_component_path_imports_nested_first.rs", + "string_add.rs", + "toplevel_ref_arg_non_rustfix.rs", + "unit_arg.rs", + "unnecessary_clone.rs", + "unnecessary_lazy_eval_unfixable.rs", + "write_literal.rs", + "write_literal_2.rs", + "write_with_newline.rs", +]; + +fn check_rustfix_coverage() { + let missing_coverage_path = Path::new("target/debug/test/ui/rustfix_missing_coverage.txt"); + + if let Ok(missing_coverage_contents) = std::fs::read_to_string(missing_coverage_path) { + assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.iter().is_sorted_by_key(Path::new)); + + for rs_path in missing_coverage_contents.lines() { + if Path::new(rs_path).starts_with("tests/ui/crashes") { + continue; + } + let filename = Path::new(rs_path).strip_prefix("tests/ui/").unwrap(); + assert!( + RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS + .binary_search_by_key(&filename, Path::new) + .is_ok(), + "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \ + Please either add `// run-rustfix` at the top of the file or add the file to \ + `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.", + rs_path, + ); + } + } +} + +#[test] +fn rustfix_coverage_known_exceptions_accuracy() { + for filename in RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS { + let rs_path = Path::new("tests/ui").join(filename); + assert!( + rs_path.exists(), + "`{}` does not exists", + rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display() + ); + let fixed_path = rs_path.with_extension("fixed"); + assert!( + !fixed_path.exists(), + "`{}` exists", + fixed_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display() + ); + } +} + /// Restores an env var on drop #[must_use] struct VarGuard { diff --git a/tests/ui-internal/unnecessary_symbol_str.fixed b/tests/ui-internal/unnecessary_symbol_str.fixed index 4f5336663a8..6033d06e4f6 100644 --- a/tests/ui-internal/unnecessary_symbol_str.fixed +++ b/tests/ui-internal/unnecessary_symbol_str.fixed @@ -2,6 +2,7 @@ #![feature(rustc_private)] #![deny(clippy::internal)] #![allow( + clippy::borrow_deref_ref, clippy::unnecessary_operation, unused_must_use, clippy::missing_clippy_version_attribute diff --git a/tests/ui-internal/unnecessary_symbol_str.rs b/tests/ui-internal/unnecessary_symbol_str.rs index 894aa1d3bc6..1bb5d55f0b6 100644 --- a/tests/ui-internal/unnecessary_symbol_str.rs +++ b/tests/ui-internal/unnecessary_symbol_str.rs @@ -2,6 +2,7 @@ #![feature(rustc_private)] #![deny(clippy::internal)] #![allow( + clippy::borrow_deref_ref, clippy::unnecessary_operation, unused_must_use, clippy::missing_clippy_version_attribute diff --git a/tests/ui-internal/unnecessary_symbol_str.stderr b/tests/ui-internal/unnecessary_symbol_str.stderr index 75367bf4bc5..a1f507f331d 100644 --- a/tests/ui-internal/unnecessary_symbol_str.stderr +++ b/tests/ui-internal/unnecessary_symbol_str.stderr @@ -1,5 +1,5 @@ error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:15:5 + --> $DIR/unnecessary_symbol_str.rs:16:5 | LL | Symbol::intern("foo").as_str() == "clippy"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy` @@ -12,25 +12,25 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:16:5 + --> $DIR/unnecessary_symbol_str.rs:17:5 | LL | Symbol::intern("foo").to_string() == "self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower` error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:17:5 + --> $DIR/unnecessary_symbol_str.rs:18:5 | LL | Symbol::intern("foo").to_ident_string() != "Self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper` error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:18:5 + --> $DIR/unnecessary_symbol_str.rs:19:5 | LL | &*Ident::empty().as_str() == "clippy"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:19:5 + --> $DIR/unnecessary_symbol_str.rs:20:5 | LL | "clippy" == Ident::empty().to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` diff --git a/tests/ui-toml/dbg_macro/clippy.toml b/tests/ui-toml/dbg_macro/clippy.toml new file mode 100644 index 00000000000..4296655a040 --- /dev/null +++ b/tests/ui-toml/dbg_macro/clippy.toml @@ -0,0 +1 @@ +allow-dbg-in-tests = true diff --git a/tests/ui-toml/dbg_macro/dbg_macro.rs b/tests/ui-toml/dbg_macro/dbg_macro.rs new file mode 100644 index 00000000000..5d9ce18f631 --- /dev/null +++ b/tests/ui-toml/dbg_macro/dbg_macro.rs @@ -0,0 +1,39 @@ +// compile-flags: --test +#![warn(clippy::dbg_macro)] + +fn foo(n: u32) -> u32 { + if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } +} + +fn factorial(n: u32) -> u32 { + if dbg!(n <= 1) { + dbg!(1) + } else { + dbg!(n * factorial(n - 1)) + } +} + +fn main() { + dbg!(42); + dbg!(dbg!(dbg!(42))); + foo(3) + dbg!(factorial(4)); + dbg!(1, 2, dbg!(3, 4)); + dbg!(1, 2, 3, 4, 5); +} + +#[test] +pub fn issue8481() { + dbg!(1); +} + +#[cfg(test)] +fn foo2() { + dbg!(1); +} + +#[cfg(test)] +mod mod1 { + fn func() { + dbg!(1); + } +} diff --git a/tests/ui-toml/dbg_macro/dbg_macro.stderr b/tests/ui-toml/dbg_macro/dbg_macro.stderr new file mode 100644 index 00000000000..46efb86dcfc --- /dev/null +++ b/tests/ui-toml/dbg_macro/dbg_macro.stderr @@ -0,0 +1,102 @@ +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:5:22 + | +LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::dbg-macro` implied by `-D warnings` +help: ensure to avoid having uses of it in version control + | +LL | if let Some(n) = n.checked_sub(4) { n } else { n } + | ~~~~~~~~~~~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:9:8 + | +LL | if dbg!(n <= 1) { + | ^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | if n <= 1 { + | ~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:10:9 + | +LL | dbg!(1) + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1 + | + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:12:9 + | +LL | dbg!(n * factorial(n - 1)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | n * factorial(n - 1) + | + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:17:5 + | +LL | dbg!(42); + | ^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 42; + | ~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:18:5 + | +LL | dbg!(dbg!(dbg!(42))); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | dbg!(dbg!(42)); + | ~~~~~~~~~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:19:14 + | +LL | foo(3) + dbg!(factorial(4)); + | ^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | foo(3) + factorial(4); + | ~~~~~~~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:20:5 + | +LL | dbg!(1, 2, dbg!(3, 4)); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | (1, 2, dbg!(3, 4)); + | ~~~~~~~~~~~~~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:21:5 + | +LL | dbg!(1, 2, 3, 4, 5); + | ^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | (1, 2, 3, 4, 5); + | ~~~~~~~~~~~~~~~ + +error: aborting due to 9 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 92838aa57df..1d87fd91a25 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,5 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of + allow-dbg-in-tests allow-expect-in-tests allow-unwrap-in-tests allowed-scripts diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs index 74d0d7c2650..0e82fb20e45 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.rs +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -1,6 +1,6 @@ // compile-flags: --test -#![allow(unused_mut, clippy::from_iter_instead_of_collect)] +#![allow(unused_mut, clippy::get_first, clippy::from_iter_instead_of_collect)] #![warn(clippy::unwrap_used)] #![deny(clippy::get_unwrap)] diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_letter_range.fixed new file mode 100644 index 00000000000..39f8f0c2949 --- /dev/null +++ b/tests/ui/almost_complete_letter_range.fixed @@ -0,0 +1,66 @@ +// run-rustfix +// edition:2018 + +#![feature(custom_inner_attributes)] +#![feature(exclusive_range_pattern)] +#![feature(stmt_expr_attributes)] +#![warn(clippy::almost_complete_letter_range)] +#![allow(ellipsis_inclusive_range_patterns)] + +macro_rules! a { + () => { + 'a' + }; +} + +fn main() { + #[rustfmt::skip] + { + let _ = ('a') ..='z'; + let _ = 'A' ..= ('Z'); + } + + let _ = 'b'..'z'; + let _ = 'B'..'Z'; + + let _ = (b'a')..=(b'z'); + let _ = b'A'..=b'Z'; + + let _ = b'b'..b'z'; + let _ = b'B'..b'Z'; + + let _ = a!()..='z'; + + let _ = match 0u8 { + b'a'..=b'z' if true => 1, + b'A'..=b'Z' if true => 2, + b'b'..b'z' => 3, + b'B'..b'Z' => 4, + _ => 5, + }; + + let _ = match 'x' { + 'a'..='z' if true => 1, + 'A'..='Z' if true => 2, + 'b'..'z' => 3, + 'B'..'Z' => 4, + _ => 5, + }; +} + +fn _under_msrv() { + #![clippy::msrv = "1.25"] + let _ = match 'a' { + 'a'...'z' => 1, + _ => 2, + }; +} + +fn _meets_msrv() { + #![clippy::msrv = "1.26"] + let _ = 'a'..='z'; + let _ = match 'a' { + 'a'..='z' => 1, + _ => 2, + }; +} diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_letter_range.rs new file mode 100644 index 00000000000..3dc02199257 --- /dev/null +++ b/tests/ui/almost_complete_letter_range.rs @@ -0,0 +1,66 @@ +// run-rustfix +// edition:2018 + +#![feature(custom_inner_attributes)] +#![feature(exclusive_range_pattern)] +#![feature(stmt_expr_attributes)] +#![warn(clippy::almost_complete_letter_range)] +#![allow(ellipsis_inclusive_range_patterns)] + +macro_rules! a { + () => { + 'a' + }; +} + +fn main() { + #[rustfmt::skip] + { + let _ = ('a') ..'z'; + let _ = 'A' .. ('Z'); + } + + let _ = 'b'..'z'; + let _ = 'B'..'Z'; + + let _ = (b'a')..(b'z'); + let _ = b'A'..b'Z'; + + let _ = b'b'..b'z'; + let _ = b'B'..b'Z'; + + let _ = a!()..'z'; + + let _ = match 0u8 { + b'a'..b'z' if true => 1, + b'A'..b'Z' if true => 2, + b'b'..b'z' => 3, + b'B'..b'Z' => 4, + _ => 5, + }; + + let _ = match 'x' { + 'a'..'z' if true => 1, + 'A'..'Z' if true => 2, + 'b'..'z' => 3, + 'B'..'Z' => 4, + _ => 5, + }; +} + +fn _under_msrv() { + #![clippy::msrv = "1.25"] + let _ = match 'a' { + 'a'..'z' => 1, + _ => 2, + }; +} + +fn _meets_msrv() { + #![clippy::msrv = "1.26"] + let _ = 'a'..'z'; + let _ = match 'a' { + 'a'..'z' => 1, + _ => 2, + }; +} diff --git a/tests/ui/almost_complete_letter_range.stderr b/tests/ui/almost_complete_letter_range.stderr new file mode 100644 index 00000000000..74980ec1a92 --- /dev/null +++ b/tests/ui/almost_complete_letter_range.stderr @@ -0,0 +1,100 @@ +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:19:17 + | +LL | let _ = ('a') ..'z'; + | ^^^^^^--^^^ + | | + | help: use an inclusive range: `..=` + | + = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:20:17 + | +LL | let _ = 'A' .. ('Z'); + | ^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:26:13 + | +LL | let _ = (b'a')..(b'z'); + | ^^^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:27:13 + | +LL | let _ = b'A'..b'Z'; + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:32:13 + | +LL | let _ = a!()..'z'; + | ^^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:35:9 + | +LL | b'a'..b'z' if true => 1, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:36:9 + | +LL | b'A'..b'Z' if true => 2, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:43:9 + | +LL | 'a'..'z' if true => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:44:9 + | +LL | 'A'..'Z' if true => 2, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:54:9 + | +LL | 'a'..'z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `...` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:61:13 + | +LL | let _ = 'a'..'z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:63:9 + | +LL | 'a'..'z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: aborting due to 12 previous errors + diff --git a/tests/ui/as_underscore.fixed b/tests/ui/as_underscore.fixed new file mode 100644 index 00000000000..948f6d8e6b1 --- /dev/null +++ b/tests/ui/as_underscore.fixed @@ -0,0 +1,13 @@ +// run-rustfix + +#![warn(clippy::as_underscore)] + +fn foo(_n: usize) {} + +fn main() { + let n: u16 = 256; + foo(n as usize); + + let n = 0_u128; + let _n: u8 = n as u8; +} diff --git a/tests/ui/as_underscore.rs b/tests/ui/as_underscore.rs new file mode 100644 index 00000000000..97785ed08a8 --- /dev/null +++ b/tests/ui/as_underscore.rs @@ -0,0 +1,13 @@ +// run-rustfix + +#![warn(clippy::as_underscore)] + +fn foo(_n: usize) {} + +fn main() { + let n: u16 = 256; + foo(n as _); + + let n = 0_u128; + let _n: u8 = n as _; +} diff --git a/tests/ui/as_underscore.stderr b/tests/ui/as_underscore.stderr new file mode 100644 index 00000000000..d7cd58d965a --- /dev/null +++ b/tests/ui/as_underscore.stderr @@ -0,0 +1,20 @@ +error: using `as _` conversion + --> $DIR/as_underscore.rs:9:9 + | +LL | foo(n as _); + | ^^^^^- + | | + | help: consider giving the type explicitly: `usize` + | + = note: `-D clippy::as-underscore` implied by `-D warnings` + +error: using `as _` conversion + --> $DIR/as_underscore.rs:12:18 + | +LL | let _n: u8 = n as _; + | ^^^^^- + | | + | help: consider giving the type explicitly: `u8` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/bind_instead_of_map_multipart.fixed b/tests/ui/bind_instead_of_map_multipart.fixed new file mode 100644 index 00000000000..e1589843226 --- /dev/null +++ b/tests/ui/bind_instead_of_map_multipart.fixed @@ -0,0 +1,62 @@ +// run-rustfix +#![deny(clippy::bind_instead_of_map)] +#![allow(clippy::blocks_in_if_conditions)] + +pub fn main() { + let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + let _ = Some("42").and_then(|s| if s.len() < 42 { None } else { Some(s.len()) }); + + let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Err(()) } else { Ok(s.len()) }); + + let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() }); + let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Ok(()) } else { Err(s.len()) }); + + hard_example(); + macro_example(); +} + +fn hard_example() { + Some("42").map(|s| { + if { + if s == "43" { + return 43; + } + s == "42" + } { + return 45; + } + match s.len() { + 10 => 2, + 20 => { + if foo() { + return { + if foo() { + return 20; + } + println!("foo"); + 3 + }; + } + 20 + }, + 40 => 30, + _ => 1, + } + }); +} + +fn foo() -> bool { + true +} + +macro_rules! m { + () => { + Some(10) + }; +} + +fn macro_example() { + let _ = Some("").and_then(|s| if s.len() == 20 { m!() } else { Some(20) }); + let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) }); +} diff --git a/tests/ui/bind_instead_of_map_multipart.rs b/tests/ui/bind_instead_of_map_multipart.rs index 91d9d11e3c1..49944403f6d 100644 --- a/tests/ui/bind_instead_of_map_multipart.rs +++ b/tests/ui/bind_instead_of_map_multipart.rs @@ -1,3 +1,4 @@ +// run-rustfix #![deny(clippy::bind_instead_of_map)] #![allow(clippy::blocks_in_if_conditions)] diff --git a/tests/ui/bind_instead_of_map_multipart.stderr b/tests/ui/bind_instead_of_map_multipart.stderr index e4f605a4de3..f822b6f49fa 100644 --- a/tests/ui/bind_instead_of_map_multipart.stderr +++ b/tests/ui/bind_instead_of_map_multipart.stderr @@ -1,11 +1,11 @@ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:5:13 + --> $DIR/bind_instead_of_map_multipart.rs:6:13 | LL | let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/bind_instead_of_map_multipart.rs:1:9 + --> $DIR/bind_instead_of_map_multipart.rs:2:9 | LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); | ~~~ ~ ~~~~~~~ error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:8:13 + --> $DIR/bind_instead_of_map_multipart.rs:9:13 | LL | let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() | ~~~ ~ ~~~~~~~ error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:11:13 + --> $DIR/bind_instead_of_map_multipart.rs:12:13 | LL | let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } | ~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:19:5 + --> $DIR/bind_instead_of_map_multipart.rs:20:5 | LL | / Some("42").and_then(|s| { LL | | if { @@ -59,7 +59,7 @@ LL | s == "42" ... error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:60:13 + --> $DIR/bind_instead_of_map_multipart.rs:61:13 | LL | let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/borrow_deref_ref.fixed b/tests/ui/borrow_deref_ref.fixed new file mode 100644 index 00000000000..bf4691c5bc9 --- /dev/null +++ b/tests/ui/borrow_deref_ref.fixed @@ -0,0 +1,59 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] + +fn main() {} + +mod should_lint { + fn one_help() { + let a = &12; + let b = a; + + let b = &mut bar(&12); + } + + fn bar(x: &u32) -> &u32 { + x + } +} + +// this mod explains why we should not lint `&mut &* (&T)` +mod should_not_lint1 { + fn foo(x: &mut &u32) { + *x = &1; + } + + fn main() { + let mut x = &0; + foo(&mut &*x); // should not lint + assert_eq!(*x, 0); + + foo(&mut x); + assert_eq!(*x, 1); + } +} + +// similar to should_not_lint1 +mod should_not_lint2 { + struct S<'a> { + a: &'a u32, + b: u32, + } + + fn main() { + let s = S { a: &1, b: 1 }; + let x = &mut &*s.a; + *x = &2; + } +} + +// this mod explains why we should not lint `& &* (&T)` +mod false_negative { + fn foo() { + let x = &12; + let addr_x = &x as *const _ as usize; + let addr_y = &x as *const _ as usize; // assert ok + // let addr_y = &x as *const _ as usize; // assert fail + assert_ne!(addr_x, addr_y); + } +} diff --git a/tests/ui/borrow_deref_ref.rs b/tests/ui/borrow_deref_ref.rs new file mode 100644 index 00000000000..28c005fdbef --- /dev/null +++ b/tests/ui/borrow_deref_ref.rs @@ -0,0 +1,59 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] + +fn main() {} + +mod should_lint { + fn one_help() { + let a = &12; + let b = &*a; + + let b = &mut &*bar(&12); + } + + fn bar(x: &u32) -> &u32 { + x + } +} + +// this mod explains why we should not lint `&mut &* (&T)` +mod should_not_lint1 { + fn foo(x: &mut &u32) { + *x = &1; + } + + fn main() { + let mut x = &0; + foo(&mut &*x); // should not lint + assert_eq!(*x, 0); + + foo(&mut x); + assert_eq!(*x, 1); + } +} + +// similar to should_not_lint1 +mod should_not_lint2 { + struct S<'a> { + a: &'a u32, + b: u32, + } + + fn main() { + let s = S { a: &1, b: 1 }; + let x = &mut &*s.a; + *x = &2; + } +} + +// this mod explains why we should not lint `& &* (&T)` +mod false_negative { + fn foo() { + let x = &12; + let addr_x = &x as *const _ as usize; + let addr_y = &&*x as *const _ as usize; // assert ok + // let addr_y = &x as *const _ as usize; // assert fail + assert_ne!(addr_x, addr_y); + } +} diff --git a/tests/ui/borrow_deref_ref.stderr b/tests/ui/borrow_deref_ref.stderr new file mode 100644 index 00000000000..d72de37c69f --- /dev/null +++ b/tests/ui/borrow_deref_ref.stderr @@ -0,0 +1,22 @@ +error: deref on an immutable reference + --> $DIR/borrow_deref_ref.rs:10:17 + | +LL | let b = &*a; + | ^^^ help: if you would like to reborrow, try removing `&*`: `a` + | + = note: `-D clippy::borrow-deref-ref` implied by `-D warnings` + +error: deref on an immutable reference + --> $DIR/borrow_deref_ref.rs:12:22 + | +LL | let b = &mut &*bar(&12); + | ^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `bar(&12)` + +error: deref on an immutable reference + --> $DIR/borrow_deref_ref.rs:55:23 + | +LL | let addr_y = &&*x as *const _ as usize; // assert ok + | ^^^ help: if you would like to reborrow, try removing `&*`: `x` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/borrow_deref_ref_unfixable.rs b/tests/ui/borrow_deref_ref_unfixable.rs new file mode 100644 index 00000000000..a8e2bbfef0f --- /dev/null +++ b/tests/ui/borrow_deref_ref_unfixable.rs @@ -0,0 +1,10 @@ +#![allow(dead_code, unused_variables)] + +fn main() {} + +mod should_lint { + fn two_helps() { + let s = &String::new(); + let x: &str = &*s; + } +} diff --git a/tests/ui/borrow_deref_ref_unfixable.stderr b/tests/ui/borrow_deref_ref_unfixable.stderr new file mode 100644 index 00000000000..738b01e7ec1 --- /dev/null +++ b/tests/ui/borrow_deref_ref_unfixable.stderr @@ -0,0 +1,18 @@ +error: deref on an immutable reference + --> $DIR/borrow_deref_ref_unfixable.rs:8:23 + | +LL | let x: &str = &*s; + | ^^^ + | + = note: `-D clippy::borrow-deref-ref` implied by `-D warnings` +help: if you would like to reborrow, try removing `&*` + | +LL | let x: &str = s; + | ~ +help: if you would like to deref, try using `&**` + | +LL | let x: &str = &**s; + | ~~~~ + +error: aborting due to previous error + diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index 4ec2465be06..a68b32b097e 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -5,4 +5,25 @@ fn main() { let x: i32 = -42; let y: u32 = x.unsigned_abs(); println!("The absolute value of {} is {}", x, y); + + let a: i32 = -3; + let _: usize = a.unsigned_abs() as usize; + let _: usize = a.unsigned_abs() as _; + let _ = a.unsigned_abs() as usize; + + let a: i64 = -3; + let _ = a.unsigned_abs() as usize; + let _ = a.unsigned_abs() as u8; + let _ = a.unsigned_abs() as u16; + let _ = a.unsigned_abs() as u32; + let _ = a.unsigned_abs(); + let _ = a.unsigned_abs() as u128; + + let a: isize = -3; + let _ = a.unsigned_abs(); + let _ = a.unsigned_abs() as u8; + let _ = a.unsigned_abs() as u16; + let _ = a.unsigned_abs() as u32; + let _ = a.unsigned_abs() as u64; + let _ = a.unsigned_abs() as u128; } diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 59b9c8c3678..110fbc6c2df 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -5,4 +5,25 @@ fn main() { let x: i32 = -42; let y: u32 = x.abs() as u32; println!("The absolute value of {} is {}", x, y); + + let a: i32 = -3; + let _: usize = a.abs() as usize; + let _: usize = a.abs() as _; + let _ = a.abs() as usize; + + let a: i64 = -3; + let _ = a.abs() as usize; + let _ = a.abs() as u8; + let _ = a.abs() as u16; + let _ = a.abs() as u32; + let _ = a.abs() as u64; + let _ = a.abs() as u128; + + let a: isize = -3; + let _ = a.abs() as usize; + let _ = a.abs() as u8; + let _ = a.abs() as u16; + let _ = a.abs() as u32; + let _ = a.abs() as u64; + let _ = a.abs() as u128; } diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index eb12857357a..02c24e10659 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -6,5 +6,95 @@ LL | let y: u32 = x.abs() as u32; | = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` -error: aborting due to previous error +error: casting the result of `i32::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:10:20 + | +LL | let _: usize = a.abs() as usize; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i32::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:11:20 + | +LL | let _: usize = a.abs() as _; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i32::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:12:13 + | +LL | let _ = a.abs() as usize; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:15:13 + | +LL | let _ = a.abs() as usize; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u8 + --> $DIR/cast_abs_to_unsigned.rs:16:13 + | +LL | let _ = a.abs() as u8; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u16 + --> $DIR/cast_abs_to_unsigned.rs:17:13 + | +LL | let _ = a.abs() as u16; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:18:13 + | +LL | let _ = a.abs() as u32; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u64 + --> $DIR/cast_abs_to_unsigned.rs:19:13 + | +LL | let _ = a.abs() as u64; + | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u128 + --> $DIR/cast_abs_to_unsigned.rs:20:13 + | +LL | let _ = a.abs() as u128; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:23:13 + | +LL | let _ = a.abs() as usize; + | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u8 + --> $DIR/cast_abs_to_unsigned.rs:24:13 + | +LL | let _ = a.abs() as u8; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u16 + --> $DIR/cast_abs_to_unsigned.rs:25:13 + | +LL | let _ = a.abs() as u16; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:26:13 + | +LL | let _ = a.abs() as u32; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u64 + --> $DIR/cast_abs_to_unsigned.rs:27:13 + | +LL | let _ = a.abs() as u64; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u128 + --> $DIR/cast_abs_to_unsigned.rs:28:13 + | +LL | let _ = a.abs() as u128; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: aborting due to 16 previous errors diff --git a/tests/ui/collapsible_match2.rs b/tests/ui/collapsible_match2.rs index 542e6948427..c8fb0a39e95 100644 --- a/tests/ui/collapsible_match2.rs +++ b/tests/ui/collapsible_match2.rs @@ -57,7 +57,7 @@ fn lint_cases(opt_opt: Option>, res_opt: Result, String> // ref pattern and deref match Some(&[1]) { - Some(ref s) => match &*s { + Some(ref s) => match s { [n] => foo(n), _ => (), }, diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index 46b645aea13..fe64e469379 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -78,7 +78,7 @@ LL | [n] => foo(n), error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:60:24 | -LL | Some(ref s) => match &*s { +LL | Some(ref s) => match s { | ________________________^ LL | | [n] => foo(n), LL | | _ => (), @@ -88,7 +88,7 @@ LL | | }, help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:60:14 | -LL | Some(ref s) => match &*s { +LL | Some(ref s) => match s { | ^^^^^ replace this binding LL | [n] => foo(n), | ^^^ with this pattern diff --git a/tests/ui/crashes/ice-8850.rs b/tests/ui/crashes/ice-8850.rs new file mode 100644 index 00000000000..f2747ab2239 --- /dev/null +++ b/tests/ui/crashes/ice-8850.rs @@ -0,0 +1,27 @@ +fn fn_pointer_static() -> usize { + static FN: fn() -> usize = || 1; + let res = FN() + 1; + res +} + +fn fn_pointer_const() -> usize { + const FN: fn() -> usize = || 1; + let res = FN() + 1; + res +} + +fn deref_to_dyn_fn() -> usize { + struct Derefs; + impl std::ops::Deref for Derefs { + type Target = dyn Fn() -> usize; + + fn deref(&self) -> &Self::Target { + &|| 2 + } + } + static FN: Derefs = Derefs; + let res = FN() + 1; + res +} + +fn main() {} diff --git a/tests/ui/crashes/ice-8850.stderr b/tests/ui/crashes/ice-8850.stderr new file mode 100644 index 00000000000..620fd1edaf7 --- /dev/null +++ b/tests/ui/crashes/ice-8850.stderr @@ -0,0 +1,45 @@ +error: returning the result of a `let` binding from a block + --> $DIR/ice-8850.rs:4:5 + | +LL | let res = FN() + 1; + | ------------------- unnecessary `let` binding +LL | res + | ^^^ + | + = note: `-D clippy::let-and-return` implied by `-D warnings` +help: return the expression directly + | +LL ~ +LL ~ FN() + 1 + | + +error: returning the result of a `let` binding from a block + --> $DIR/ice-8850.rs:10:5 + | +LL | let res = FN() + 1; + | ------------------- unnecessary `let` binding +LL | res + | ^^^ + | +help: return the expression directly + | +LL ~ +LL ~ FN() + 1 + | + +error: returning the result of a `let` binding from a block + --> $DIR/ice-8850.rs:24:5 + | +LL | let res = FN() + 1; + | ------------------- unnecessary `let` binding +LL | res + | ^^^ + | +help: return the expression directly + | +LL ~ +LL ~ FN() + 1 + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui/dbg_macro.stderr b/tests/ui/dbg_macro.stderr index a3e7a7162e5..e6a65b46d97 100644 --- a/tests/ui/dbg_macro.stderr +++ b/tests/ui/dbg_macro.stderr @@ -109,5 +109,38 @@ help: ensure to avoid having uses of it in version control LL | 2; | ~ -error: aborting due to 10 previous errors +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:47:5 + | +LL | dbg!(1); + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1; + | ~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:52:5 + | +LL | dbg!(1); + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1; + | ~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:58:9 + | +LL | dbg!(1); + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1; + | ~ + +error: aborting due to 13 previous errors diff --git a/tests/ui/debug_assert_with_mut_call.rs b/tests/ui/debug_assert_with_mut_call.rs index c5de4125565..46faa0a7b91 100644 --- a/tests/ui/debug_assert_with_mut_call.rs +++ b/tests/ui/debug_assert_with_mut_call.rs @@ -1,7 +1,7 @@ #![feature(custom_inner_attributes)] #![rustfmt::skip] #![warn(clippy::debug_assert_with_mut_call)] -#![allow(clippy::redundant_closure_call)] +#![allow(clippy::redundant_closure_call, clippy::get_first)] struct S; diff --git a/tests/ui/deref_by_slicing.fixed b/tests/ui/deref_by_slicing.fixed index b26276218b7..257393e56ff 100644 --- a/tests/ui/deref_by_slicing.fixed +++ b/tests/ui/deref_by_slicing.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::deref_by_slicing)] +#![allow(clippy::borrow_deref_ref)] use std::io::Read; diff --git a/tests/ui/deref_by_slicing.rs b/tests/ui/deref_by_slicing.rs index 6aa1408ba17..e288046f927 100644 --- a/tests/ui/deref_by_slicing.rs +++ b/tests/ui/deref_by_slicing.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::deref_by_slicing)] +#![allow(clippy::borrow_deref_ref)] use std::io::Read; diff --git a/tests/ui/deref_by_slicing.stderr b/tests/ui/deref_by_slicing.stderr index ffd76de378d..8f042ef47eb 100644 --- a/tests/ui/deref_by_slicing.stderr +++ b/tests/ui/deref_by_slicing.stderr @@ -1,5 +1,5 @@ error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:9:13 + --> $DIR/deref_by_slicing.rs:10:13 | LL | let _ = &vec[..]; | ^^^^^^^^ help: dereference the original value instead: `&*vec` @@ -7,49 +7,49 @@ LL | let _ = &vec[..]; = note: `-D clippy::deref-by-slicing` implied by `-D warnings` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:10:13 + --> $DIR/deref_by_slicing.rs:11:13 | LL | let _ = &mut vec[..]; | ^^^^^^^^^^^^ help: dereference the original value instead: `&mut *vec` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:13:13 + --> $DIR/deref_by_slicing.rs:14:13 | LL | let _ = &ref_vec[..]; | ^^^^^^^^^^^^ help: dereference the original value instead: `&**ref_vec` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:14:21 + --> $DIR/deref_by_slicing.rs:15:21 | LL | let mut_slice = &mut ref_vec[..]; | ^^^^^^^^^^^^^^^^ help: dereference the original value instead: `&mut **ref_vec` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:15:13 + --> $DIR/deref_by_slicing.rs:16:13 | LL | let _ = &mut mut_slice[..]; // Err, re-borrows slice | ^^^^^^^^^^^^^^^^^^ help: reborrow the original value instead: `&mut *mut_slice` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:18:13 + --> $DIR/deref_by_slicing.rs:19:13 | LL | let _ = &s[..]; | ^^^^^^ help: dereference the original value instead: `&*s` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:21:18 + --> $DIR/deref_by_slicing.rs:22:18 | LL | let _ = &mut &S[..]; // Err, re-borrows slice | ^^^^^^ help: reborrow the original value instead: `&*S` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:25:13 + --> $DIR/deref_by_slicing.rs:26:13 | LL | let _ = &slice_ref[..]; // Err, derefs slice | ^^^^^^^^^^^^^^ help: dereference the original value instead: `*slice_ref` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:28:13 + --> $DIR/deref_by_slicing.rs:29:13 | LL | let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice | ^^^^^^^^^^^^ help: reborrow the original value instead: `(&*bytes)` diff --git a/tests/ui/derive_partial_eq_without_eq.fixed b/tests/ui/derive_partial_eq_without_eq.fixed index 7d4d1b3b649..012780258fc 100644 --- a/tests/ui/derive_partial_eq_without_eq.fixed +++ b/tests/ui/derive_partial_eq_without_eq.fixed @@ -95,4 +95,10 @@ enum EnumNotEq { #[derive(Debug, PartialEq, Eq, Clone)] struct RustFixWithOtherDerives; +#[derive(PartialEq)] +struct Generic(T); + +#[derive(PartialEq, Eq)] +struct GenericPhantom(core::marker::PhantomData); + fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.rs b/tests/ui/derive_partial_eq_without_eq.rs index ab4e1df1ca4..fc8285b0c6b 100644 --- a/tests/ui/derive_partial_eq_without_eq.rs +++ b/tests/ui/derive_partial_eq_without_eq.rs @@ -95,4 +95,10 @@ enum EnumNotEq { #[derive(Debug, PartialEq, Clone)] struct RustFixWithOtherDerives; +#[derive(PartialEq)] +struct Generic(T); + +#[derive(PartialEq, Eq)] +struct GenericPhantom(core::marker::PhantomData); + fn main() {} diff --git a/tests/ui/doc_link_with_quotes.rs b/tests/ui/doc_link_with_quotes.rs new file mode 100644 index 00000000000..ab52fb1a4a6 --- /dev/null +++ b/tests/ui/doc_link_with_quotes.rs @@ -0,0 +1,12 @@ +#![warn(clippy::doc_link_with_quotes)] + +fn main() { + foo() +} + +/// Calls ['bar'] +pub fn foo() { + bar() +} + +pub fn bar() {} diff --git a/tests/ui/doc_link_with_quotes.stderr b/tests/ui/doc_link_with_quotes.stderr new file mode 100644 index 00000000000..bf6d57d8afe --- /dev/null +++ b/tests/ui/doc_link_with_quotes.stderr @@ -0,0 +1,10 @@ +error: possible intra-doc link using quotes instead of backticks + --> $DIR/doc_link_with_quotes.rs:7:1 + | +LL | /// Calls ['bar'] + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::doc-link-with-quotes` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/empty_line_after_outer_attribute.rs b/tests/ui/empty_line_after_outer_attribute.rs index 3e92bca986a..d15c84d7438 100644 --- a/tests/ui/empty_line_after_outer_attribute.rs +++ b/tests/ui/empty_line_after_outer_attribute.rs @@ -4,6 +4,9 @@ #![feature(custom_inner_attributes)] #![rustfmt::skip] +#[macro_use] +extern crate clap; + #[macro_use] extern crate proc_macro_attr; @@ -110,4 +113,11 @@ pub trait Bazz { } } +#[derive(clap::Parser)] +#[clap(after_help = "This ia a help message. + +You're welcome. +")] +pub struct Args; + fn main() {} diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr index 594fca44a32..acc3edef9b9 100644 --- a/tests/ui/empty_line_after_outer_attribute.stderr +++ b/tests/ui/empty_line_after_outer_attribute.stderr @@ -1,5 +1,5 @@ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:11:1 + --> $DIR/empty_line_after_outer_attribute.rs:14:1 | LL | / #[crate_type = "lib"] LL | | @@ -10,7 +10,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) } = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:23:1 + --> $DIR/empty_line_after_outer_attribute.rs:26:1 | LL | / #[crate_type = "lib"] LL | | @@ -18,7 +18,7 @@ LL | | fn with_one_newline() { assert!(true) } | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:28:1 + --> $DIR/empty_line_after_outer_attribute.rs:31:1 | LL | / #[crate_type = "lib"] LL | | @@ -27,7 +27,7 @@ LL | | fn with_two_newlines() { assert!(true) } | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:35:1 + --> $DIR/empty_line_after_outer_attribute.rs:38:1 | LL | / #[crate_type = "lib"] LL | | @@ -35,7 +35,7 @@ LL | | enum Baz { | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:43:1 + --> $DIR/empty_line_after_outer_attribute.rs:46:1 | LL | / #[crate_type = "lib"] LL | | @@ -43,7 +43,7 @@ LL | | struct Foo { | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:51:1 + --> $DIR/empty_line_after_outer_attribute.rs:54:1 | LL | / #[crate_type = "lib"] LL | | diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 3de2a51ffa5..92f27e68549 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -1,6 +1,11 @@ // run-rustfix -#![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)] +#![allow( + unused_variables, + clippy::clone_double_ref, + clippy::needless_borrow, + clippy::borrow_deref_ref +)] #![warn(clippy::explicit_deref_methods)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index a08d7596422..d118607f992 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -1,6 +1,11 @@ // run-rustfix -#![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)] +#![allow( + unused_variables, + clippy::clone_double_ref, + clippy::needless_borrow, + clippy::borrow_deref_ref +)] #![warn(clippy::explicit_deref_methods)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index 8035d77d18d..8e8b358972b 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:30:19 + --> $DIR/explicit_deref_methods.rs:35:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try this: `&*a` @@ -7,67 +7,67 @@ LL | let b: &str = a.deref(); = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` error: explicit `deref_mut` method call - --> $DIR/explicit_deref_methods.rs:32:23 + --> $DIR/explicit_deref_methods.rs:37:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try this: `&mut **a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:35:39 + --> $DIR/explicit_deref_methods.rs:40:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:35:50 + --> $DIR/explicit_deref_methods.rs:40:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:37:20 + --> $DIR/explicit_deref_methods.rs:42:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:40:11 + --> $DIR/explicit_deref_methods.rs:45:11 | LL | match a.deref() { | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:44:28 + --> $DIR/explicit_deref_methods.rs:49:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:46:13 + --> $DIR/explicit_deref_methods.rs:51:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:48:28 + --> $DIR/explicit_deref_methods.rs:53:28 | LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:50:19 + --> $DIR/explicit_deref_methods.rs:55:19 | LL | let b: &str = a.deref().deref(); | ^^^^^^^^^^^^^^^^^ help: try this: `&**a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:53:13 + --> $DIR/explicit_deref_methods.rs:58:13 | LL | let b = opt_a.unwrap().deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:79:31 + --> $DIR/explicit_deref_methods.rs:84:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try this: `&*a` diff --git a/tests/ui/forget_ref.rs b/tests/ui/forget_ref.rs index 6c8c4c9c0ed..031b415f56f 100644 --- a/tests/ui/forget_ref.rs +++ b/tests/ui/forget_ref.rs @@ -1,6 +1,7 @@ #![warn(clippy::forget_ref)] #![allow(clippy::toplevel_ref_arg)] #![allow(clippy::unnecessary_wraps, clippy::forget_non_drop)] +#![allow(clippy::borrow_deref_ref)] use std::mem::forget; diff --git a/tests/ui/forget_ref.stderr b/tests/ui/forget_ref.stderr index 73409388ed1..df5cd8cacdb 100644 --- a/tests/ui/forget_ref.stderr +++ b/tests/ui/forget_ref.stderr @@ -1,108 +1,108 @@ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:10:5 + --> $DIR/forget_ref.rs:11:5 | LL | forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::forget-ref` implied by `-D warnings` note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:10:12 + --> $DIR/forget_ref.rs:11:12 | LL | forget(&SomeStruct); | ^^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:13:5 + --> $DIR/forget_ref.rs:14:5 | LL | forget(&owned); | ^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:13:12 + --> $DIR/forget_ref.rs:14:12 | LL | forget(&owned); | ^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:14:5 + --> $DIR/forget_ref.rs:15:5 | LL | forget(&&owned); | ^^^^^^^^^^^^^^^ | note: argument has type `&&SomeStruct` - --> $DIR/forget_ref.rs:14:12 + --> $DIR/forget_ref.rs:15:12 | LL | forget(&&owned); | ^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:15:5 + --> $DIR/forget_ref.rs:16:5 | LL | forget(&mut owned); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/forget_ref.rs:15:12 + --> $DIR/forget_ref.rs:16:12 | LL | forget(&mut owned); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:19:5 + --> $DIR/forget_ref.rs:20:5 | LL | forget(&*reference1); | ^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:19:12 + --> $DIR/forget_ref.rs:20:12 | LL | forget(&*reference1); | ^^^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:22:5 + --> $DIR/forget_ref.rs:23:5 | LL | forget(reference2); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/forget_ref.rs:22:12 + --> $DIR/forget_ref.rs:23:12 | LL | forget(reference2); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:25:5 + --> $DIR/forget_ref.rs:26:5 | LL | forget(reference3); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:25:12 + --> $DIR/forget_ref.rs:26:12 | LL | forget(reference3); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:30:5 + --> $DIR/forget_ref.rs:31:5 | LL | forget(&val); | ^^^^^^^^^^^^ | note: argument has type `&T` - --> $DIR/forget_ref.rs:30:12 + --> $DIR/forget_ref.rs:31:12 | LL | forget(&val); | ^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:38:5 + --> $DIR/forget_ref.rs:39:5 | LL | std::mem::forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:38:22 + --> $DIR/forget_ref.rs:39:22 | LL | std::mem::forget(&SomeStruct); | ^^^^^^^^^^^ diff --git a/tests/ui/get_first.fixed b/tests/ui/get_first.fixed new file mode 100644 index 00000000000..def58afa4fb --- /dev/null +++ b/tests/ui/get_first.fixed @@ -0,0 +1,42 @@ +// run-rustfix +#![warn(clippy::get_first)] +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; + +struct Bar { + arr: [u32; 3], +} + +impl Bar { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } +} + +fn main() { + let x = vec![2, 3, 5]; + let _ = x.first(); // Use x.first() + let _ = x.get(1); + let _ = x[0]; + + let y = [2, 3, 5]; + let _ = y.first(); // Use y.first() + let _ = y.get(1); + let _ = y[0]; + + let z = &[2, 3, 5]; + let _ = z.first(); // Use z.first() + let _ = z.get(1); + let _ = z[0]; + + let vecdeque: VecDeque<_> = x.iter().cloned().collect(); + let hashmap: HashMap = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]); + let btreemap: BTreeMap = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]); + let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice. + let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice. + let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice. + + let bar = Bar { arr: [0, 1, 2] }; + let _ = bar.get(0); // Do not lint, because Bar is struct. +} diff --git a/tests/ui/get_first.rs b/tests/ui/get_first.rs new file mode 100644 index 00000000000..85a381854cd --- /dev/null +++ b/tests/ui/get_first.rs @@ -0,0 +1,42 @@ +// run-rustfix +#![warn(clippy::get_first)] +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; + +struct Bar { + arr: [u32; 3], +} + +impl Bar { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } +} + +fn main() { + let x = vec![2, 3, 5]; + let _ = x.get(0); // Use x.first() + let _ = x.get(1); + let _ = x[0]; + + let y = [2, 3, 5]; + let _ = y.get(0); // Use y.first() + let _ = y.get(1); + let _ = y[0]; + + let z = &[2, 3, 5]; + let _ = z.get(0); // Use z.first() + let _ = z.get(1); + let _ = z[0]; + + let vecdeque: VecDeque<_> = x.iter().cloned().collect(); + let hashmap: HashMap = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]); + let btreemap: BTreeMap = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]); + let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice. + let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice. + let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice. + + let bar = Bar { arr: [0, 1, 2] }; + let _ = bar.get(0); // Do not lint, because Bar is struct. +} diff --git a/tests/ui/get_first.stderr b/tests/ui/get_first.stderr new file mode 100644 index 00000000000..466beff9c92 --- /dev/null +++ b/tests/ui/get_first.stderr @@ -0,0 +1,22 @@ +error: accessing first element with `x.get(0)` + --> $DIR/get_first.rs:19:13 + | +LL | let _ = x.get(0); // Use x.first() + | ^^^^^^^^ help: try: `x.first()` + | + = note: `-D clippy::get-first` implied by `-D warnings` + +error: accessing first element with `y.get(0)` + --> $DIR/get_first.rs:24:13 + | +LL | let _ = y.get(0); // Use y.first() + | ^^^^^^^^ help: try: `y.first()` + +error: accessing first element with `z.get(0)` + --> $DIR/get_first.rs:29:13 + | +LL | let _ = z.get(0); // Use z.first() + | ^^^^^^^^ help: try: `z.first()` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/get_last_with_len.fixed b/tests/ui/get_last_with_len.fixed index c8b363f9c38..1e90b37687a 100644 --- a/tests/ui/get_last_with_len.fixed +++ b/tests/ui/get_last_with_len.fixed @@ -1,10 +1,13 @@ // run-rustfix #![warn(clippy::get_last_with_len)] +#![allow(unused)] + +use std::collections::VecDeque; fn dont_use_last() { let x = vec![2, 3, 5]; - let _ = x.last(); // ~ERROR Use x.last() + let _ = x.last(); } fn indexing_two_from_end() { @@ -23,9 +26,24 @@ fn use_last_with_different_vec_length() { let _ = x.get(y.len() - 1); } -fn main() { - dont_use_last(); - indexing_two_from_end(); - index_into_last(); - use_last_with_different_vec_length(); +struct S { + field: Vec, +} + +fn in_field(s: &S) { + let _ = s.field.last(); +} + +fn main() { + let slice = &[1, 2, 3]; + let _ = slice.last(); + + let array = [4, 5, 6]; + let _ = array.last(); + + let deq = VecDeque::from([7, 8, 9]); + let _ = deq.back(); + + let nested = [[1]]; + let _ = nested[0].last(); } diff --git a/tests/ui/get_last_with_len.rs b/tests/ui/get_last_with_len.rs index bf9cb2d7e0c..d63a731bd52 100644 --- a/tests/ui/get_last_with_len.rs +++ b/tests/ui/get_last_with_len.rs @@ -1,10 +1,13 @@ // run-rustfix #![warn(clippy::get_last_with_len)] +#![allow(unused)] + +use std::collections::VecDeque; fn dont_use_last() { let x = vec![2, 3, 5]; - let _ = x.get(x.len() - 1); // ~ERROR Use x.last() + let _ = x.get(x.len() - 1); } fn indexing_two_from_end() { @@ -23,9 +26,24 @@ fn use_last_with_different_vec_length() { let _ = x.get(y.len() - 1); } -fn main() { - dont_use_last(); - indexing_two_from_end(); - index_into_last(); - use_last_with_different_vec_length(); +struct S { + field: Vec, +} + +fn in_field(s: &S) { + let _ = s.field.get(s.field.len() - 1); +} + +fn main() { + let slice = &[1, 2, 3]; + let _ = slice.get(slice.len() - 1); + + let array = [4, 5, 6]; + let _ = array.get(array.len() - 1); + + let deq = VecDeque::from([7, 8, 9]); + let _ = deq.get(deq.len() - 1); + + let nested = [[1]]; + let _ = nested[0].get(nested[0].len() - 1); } diff --git a/tests/ui/get_last_with_len.stderr b/tests/ui/get_last_with_len.stderr index 55baf87384a..ac8dd6c2e41 100644 --- a/tests/ui/get_last_with_len.stderr +++ b/tests/ui/get_last_with_len.stderr @@ -1,10 +1,40 @@ error: accessing last element with `x.get(x.len() - 1)` - --> $DIR/get_last_with_len.rs:7:13 + --> $DIR/get_last_with_len.rs:10:13 | -LL | let _ = x.get(x.len() - 1); // ~ERROR Use x.last() +LL | let _ = x.get(x.len() - 1); | ^^^^^^^^^^^^^^^^^^ help: try: `x.last()` | = note: `-D clippy::get-last-with-len` implied by `-D warnings` -error: aborting due to previous error +error: accessing last element with `s.field.get(s.field.len() - 1)` + --> $DIR/get_last_with_len.rs:34:13 + | +LL | let _ = s.field.get(s.field.len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.field.last()` + +error: accessing last element with `slice.get(slice.len() - 1)` + --> $DIR/get_last_with_len.rs:39:13 + | +LL | let _ = slice.get(slice.len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice.last()` + +error: accessing last element with `array.get(array.len() - 1)` + --> $DIR/get_last_with_len.rs:42:13 + | +LL | let _ = array.get(array.len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array.last()` + +error: accessing last element with `deq.get(deq.len() - 1)` + --> $DIR/get_last_with_len.rs:45:13 + | +LL | let _ = deq.get(deq.len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `deq.back()` + +error: accessing last element with `nested[0].get(nested[0].len() - 1)` + --> $DIR/get_last_with_len.rs:48:13 + | +LL | let _ = nested[0].get(nested[0].len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `nested[0].last()` + +error: aborting due to 6 previous errors diff --git a/tests/ui/get_unwrap.fixed b/tests/ui/get_unwrap.fixed index 8f165d67589..5827fc7d76e 100644 --- a/tests/ui/get_unwrap.fixed +++ b/tests/ui/get_unwrap.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_mut, clippy::from_iter_instead_of_collect)] +#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)] #![warn(clippy::unwrap_used)] #![deny(clippy::get_unwrap)] diff --git a/tests/ui/get_unwrap.rs b/tests/ui/get_unwrap.rs index 786749daa74..a2a323c14fb 100644 --- a/tests/ui/get_unwrap.rs +++ b/tests/ui/get_unwrap.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_mut, clippy::from_iter_instead_of_collect)] +#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)] #![warn(clippy::unwrap_used)] #![deny(clippy::get_unwrap)] diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed new file mode 100644 index 00000000000..5f9cebe212a --- /dev/null +++ b/tests/ui/identity_op.fixed @@ -0,0 +1,119 @@ +// run-rustfix + +#![warn(clippy::identity_op)] +#![allow( + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref, + clippy::double_parens, + unused +)] + +use std::fmt::Write as _; + +const ONE: i64 = 1; +const NEG_ONE: i64 = -1; +const ZERO: i64 = 0; + +struct A(String); + +impl std::ops::Shl for A { + type Output = A; + fn shl(mut self, other: i32) -> Self { + let _ = write!(self.0, "{}", other); + self + } +} + +struct Length(u8); +struct Meter; + +impl core::ops::Mul for u8 { + type Output = Length; + fn mul(self, _: Meter) -> Length { + Length(self) + } +} + +#[rustfmt::skip] +fn main() { + let x = 0; + + x; + x; + x + 1; + x; + 1 + x; + x - ZERO; //no error, as we skip lookups (for now) + x; + ((ZERO)) | x; //no error, as we skip lookups (for now) + + x; + x; + x / ONE; //no error, as we skip lookups (for now) + + x / 2; //no false positive + + x & NEG_ONE; //no error, as we skip lookups (for now) + x; + + let u: u8 = 0; + u; + + 1 << 0; // no error, this case is allowed, see issue 3430 + 42; + 1; + 42; + &x; + x; + + let mut a = A("".into()); + let b = a << 0; // no error: non-integer + + 1 * Meter; // no error: non-integer + + 2; + -2; + 2 + x; + -2 + x; + x + 1; + (x + 1) % 3; // no error + 4 % 3; // no error + 4 % -3; // no error + + // See #8724 + let a = 0; + let b = true; + (if b { 1 } else { 2 }); + (if b { 1 } else { 2 }) + if b { 3 } else { 4 }; + (match a { 0 => 10, _ => 20 }); + (match a { 0 => 10, _ => 20 }) + match a { 0 => 30, _ => 40 }; + (if b { 1 } else { 2 }) + match a { 0 => 30, _ => 40 }; + (match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 }; + (if b { 1 } else { 2 }); + + ({ a }) + 3; + ({ a } * 2); + (loop { let mut c = 0; if c == 10 { break c; } c += 1; }) + { a * 2 }; + + fn f(_: i32) { + todo!(); + } + f(a + { 8 * 5 }); + f(if b { 1 } else { 2 } + 3); + const _: i32 = { 2 * 4 } + 3; + const _: i32 = { 1 + 2 * 3 } + 3; + + a as usize; + let _ = a as usize; + ({ a } as usize); + + 2 * { a }; + (({ a } + 4)); + 1; +} + +pub fn decide(a: bool, b: bool) -> u32 { + (if a { 1 } else { 2 }) + if b { 3 } else { 5 } +} diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index fec54d00ccb..ca799c9cfac 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -1,3 +1,15 @@ +// run-rustfix + +#![warn(clippy::identity_op)] +#![allow( + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref, + clippy::double_parens, + unused +)] + use std::fmt::Write as _; const ONE: i64 = 1; @@ -24,14 +36,6 @@ impl core::ops::Mul for u8 { } } -#[allow( - clippy::eq_op, - clippy::no_effect, - clippy::unnecessary_operation, - clippy::op_ref, - clippy::double_parens -)] -#[warn(clippy::identity_op)] #[rustfmt::skip] fn main() { let x = 0; @@ -82,29 +86,34 @@ fn main() { let a = 0; let b = true; 0 + if b { 1 } else { 2 }; - 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; 0 + match a { 0 => 10, _ => 20 }; - 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error - 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error - 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error - - 0 + if b { 0 + 1 } else { 2 }; - 0 + match a { 0 => 0 + 10, _ => 20 }; - 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; + 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; + 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; + (if b { 1 } else { 2 }) + 0; - let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; - let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + 0 + { a } + 3; + 0 + { a } * 2; + 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; - 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; - - 0 + { a } + 3; // no error - 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error - fn f(_: i32) { todo!(); } f(1 * a + { 8 * 5 }); - f(0 + if b { 1 } else { 2 } + 3); // no error + f(0 + if b { 1 } else { 2 } + 3); const _: i32 = { 2 * 4 } + 0 + 3; - const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error + const _: i32 = 0 + { 1 + 2 * 3 } + 3; + + 0 + a as usize; + let _ = 0 + a as usize; + 0 + { a } as usize; + + 2 * (0 + { a }); + 1 * ({ a } + 4); + 1 * 1; +} + +pub fn decide(a: bool, b: bool) -> u32 { + 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index d8cb65839cb..1a104a20b84 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -1,202 +1,238 @@ -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:39:5 +error: this operation has no effect + --> $DIR/identity_op.rs:43:5 | LL | x + 0; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `x` | = note: `-D clippy::identity-op` implied by `-D warnings` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:40:5 +error: this operation has no effect + --> $DIR/identity_op.rs:44:5 | LL | x + (1 - 1); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:42:5 +error: this operation has no effect + --> $DIR/identity_op.rs:46:5 | LL | 0 + x; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:45:5 - | -LL | x | (0); - | ^^^^^^^ - -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:48:5 - | -LL | x * 1; - | ^^^^^ - -error: the operation is ineffective. Consider reducing it to `x` +error: this operation has no effect --> $DIR/identity_op.rs:49:5 | -LL | 1 * x; - | ^^^^^ +LL | x | (0); + | ^^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:55:5 +error: this operation has no effect + --> $DIR/identity_op.rs:52:5 + | +LL | x * 1; + | ^^^^^ help: consider reducing it to: `x` + +error: this operation has no effect + --> $DIR/identity_op.rs:53:5 + | +LL | 1 * x; + | ^^^^^ help: consider reducing it to: `x` + +error: this operation has no effect + --> $DIR/identity_op.rs:59:5 | LL | -1 & x; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `u` - --> $DIR/identity_op.rs:58:5 - | -LL | u & 255; - | ^^^^^^^ - -error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:61:5 - | -LL | 42 << 0; - | ^^^^^^^ - -error: the operation is ineffective. Consider reducing it to `1` +error: this operation has no effect --> $DIR/identity_op.rs:62:5 | -LL | 1 >> 0; - | ^^^^^^ +LL | u & 255; + | ^^^^^^^ help: consider reducing it to: `u` -error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:63:5 - | -LL | 42 >> 0; - | ^^^^^^^ - -error: the operation is ineffective. Consider reducing it to `&x` - --> $DIR/identity_op.rs:64:5 - | -LL | &x >> 0; - | ^^^^^^^ - -error: the operation is ineffective. Consider reducing it to `x` +error: this operation has no effect --> $DIR/identity_op.rs:65:5 | -LL | x >> &0; - | ^^^^^^^ +LL | 42 << 0; + | ^^^^^^^ help: consider reducing it to: `42` -error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:72:5 +error: this operation has no effect + --> $DIR/identity_op.rs:66:5 + | +LL | 1 >> 0; + | ^^^^^^ help: consider reducing it to: `1` + +error: this operation has no effect + --> $DIR/identity_op.rs:67:5 + | +LL | 42 >> 0; + | ^^^^^^^ help: consider reducing it to: `42` + +error: this operation has no effect + --> $DIR/identity_op.rs:68:5 + | +LL | &x >> 0; + | ^^^^^^^ help: consider reducing it to: `&x` + +error: this operation has no effect + --> $DIR/identity_op.rs:69:5 + | +LL | x >> &0; + | ^^^^^^^ help: consider reducing it to: `x` + +error: this operation has no effect + --> $DIR/identity_op.rs:76:5 | LL | 2 % 3; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `2` -error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:73:5 +error: this operation has no effect + --> $DIR/identity_op.rs:77:5 | LL | -2 % 3; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `-2` -error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:74:5 +error: this operation has no effect + --> $DIR/identity_op.rs:78:5 | LL | 2 % -3 + x; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `2` -error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:75:5 +error: this operation has no effect + --> $DIR/identity_op.rs:79:5 | LL | -2 % -3 + x; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `-2` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:76:9 +error: this operation has no effect + --> $DIR/identity_op.rs:80:9 | LL | x + 1 % 3; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `1` -error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }` - --> $DIR/identity_op.rs:84:5 +error: this operation has no effect + --> $DIR/identity_op.rs:88:5 | LL | 0 + if b { 1 } else { 2 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` -error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }` - --> $DIR/identity_op.rs:86:5 +error: this operation has no effect + --> $DIR/identity_op.rs:89:5 + | +LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` + +error: this operation has no effect + --> $DIR/identity_op.rs:90:5 | LL | 0 + match a { 0 => 10, _ => 20 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` -error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }` +error: this operation has no effect --> $DIR/identity_op.rs:91:5 | -LL | 0 + if b { 0 + 1 } else { 2 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:91:16 - | -LL | 0 + if b { 0 + 1 } else { 2 }; - | ^^^^^ - -error: the operation is ineffective. Consider reducing it to `match a { 0 => 0 + 10, _ => 20 }` +error: this operation has no effect --> $DIR/identity_op.rs:92:5 | -LL | 0 + match a { 0 => 0 + 10, _ => 20 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` -error: the operation is ineffective. Consider reducing it to `10` - --> $DIR/identity_op.rs:92:25 +error: this operation has no effect + --> $DIR/identity_op.rs:93:5 | -LL | 0 + match a { 0 => 0 + 10, _ => 20 }; - | ^^^^^^ +LL | 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:93:16 +error: this operation has no effect + --> $DIR/identity_op.rs:94:5 | -LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; - | ^^^^^ +LL | (if b { 1 } else { 2 }) + 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` -error: the operation is ineffective. Consider reducing it to `30` - --> $DIR/identity_op.rs:93:52 +error: this operation has no effect + --> $DIR/identity_op.rs:96:5 | -LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; - | ^^^^^^ +LL | 0 + { a } + 3; + | ^^^^^^^^^ help: consider reducing it to: `({ a })` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:95:20 +error: this operation has no effect + --> $DIR/identity_op.rs:97:5 | -LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; - | ^^^^^ +LL | 0 + { a } * 2; + | ^^^^^^^^^^^^^ help: consider reducing it to: `({ a } * 2)` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:95:52 - | -LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; - | ^^^^^ - -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:96:23 - | -LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; - | ^^^^^ - -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:96:58 - | -LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; - | ^^^^^ - -error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` +error: this operation has no effect --> $DIR/identity_op.rs:98:5 | -LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(loop { let mut c = 0; if c == 10 { break c; } c += 1; })` -error: the operation is ineffective. Consider reducing it to `a` - --> $DIR/identity_op.rs:106:7 +error: this operation has no effect + --> $DIR/identity_op.rs:103:7 | LL | f(1 * a + { 8 * 5 }); - | ^^^^^ + | ^^^^^ help: consider reducing it to: `a` -error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }` - --> $DIR/identity_op.rs:108:20 +error: this operation has no effect + --> $DIR/identity_op.rs:104:7 + | +LL | f(0 + if b { 1 } else { 2 } + 3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `if b { 1 } else { 2 }` + +error: this operation has no effect + --> $DIR/identity_op.rs:105:20 | LL | const _: i32 = { 2 * 4 } + 0 + 3; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: consider reducing it to: `{ 2 * 4 }` -error: aborting due to 33 previous errors +error: this operation has no effect + --> $DIR/identity_op.rs:106:20 + | +LL | const _: i32 = 0 + { 1 + 2 * 3 } + 3; + | ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `{ 1 + 2 * 3 }` + +error: this operation has no effect + --> $DIR/identity_op.rs:108:5 + | +LL | 0 + a as usize; + | ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize` + +error: this operation has no effect + --> $DIR/identity_op.rs:109:13 + | +LL | let _ = 0 + a as usize; + | ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize` + +error: this operation has no effect + --> $DIR/identity_op.rs:110:5 + | +LL | 0 + { a } as usize; + | ^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `({ a } as usize)` + +error: this operation has no effect + --> $DIR/identity_op.rs:112:9 + | +LL | 2 * (0 + { a }); + | ^^^^^^^^^^^ help: consider reducing it to: `{ a }` + +error: this operation has no effect + --> $DIR/identity_op.rs:113:5 + | +LL | 1 * ({ a } + 4); + | ^^^^^^^^^^^^^^^ help: consider reducing it to: `(({ a } + 4))` + +error: this operation has no effect + --> $DIR/identity_op.rs:114:5 + | +LL | 1 * 1; + | ^^^^^ help: consider reducing it to: `1` + +error: this operation has no effect + --> $DIR/identity_op.rs:118:5 + | +LL | 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })` + +error: aborting due to 39 previous errors diff --git a/tests/ui/implicit_clone.fixed b/tests/ui/implicit_clone.fixed new file mode 100644 index 00000000000..33770fc2a2c --- /dev/null +++ b/tests/ui/implicit_clone.fixed @@ -0,0 +1,118 @@ +// run-rustfix +#![warn(clippy::implicit_clone)] +#![allow(clippy::clone_on_copy, clippy::redundant_clone)] +use std::borrow::Borrow; +use std::ffi::{OsStr, OsString}; +use std::path::PathBuf; + +fn return_owned_from_slice(slice: &[u32]) -> Vec { + slice.to_owned() +} + +pub fn own_same(v: T) -> T +where + T: ToOwned, +{ + v.to_owned() +} + +pub fn own_same_from_ref(v: &T) -> T +where + T: ToOwned, +{ + v.to_owned() +} + +pub fn own_different(v: T) -> U +where + T: ToOwned, +{ + v.to_owned() +} + +#[derive(Copy, Clone)] +struct Kitten; +impl Kitten { + // badly named method + fn to_vec(self) -> Kitten { + Kitten {} + } +} +impl Borrow for Kitten { + fn borrow(&self) -> &BorrowedKitten { + static VALUE: BorrowedKitten = BorrowedKitten {}; + &VALUE + } +} + +struct BorrowedKitten; +impl ToOwned for BorrowedKitten { + type Owned = Kitten; + fn to_owned(&self) -> Kitten { + Kitten {} + } +} + +mod weird { + #[allow(clippy::ptr_arg)] + pub fn to_vec(v: &Vec) -> Vec { + v.clone() + } +} + +fn main() { + let vec = vec![5]; + let _ = return_owned_from_slice(&vec); + let _ = vec.clone(); + let _ = vec.clone(); + + let vec_ref = &vec; + let _ = return_owned_from_slice(vec_ref); + let _ = vec_ref.clone(); + let _ = vec_ref.clone(); + + // we expect no lint for this + let _ = weird::to_vec(&vec); + + // we expect no lints for this + let slice: &[u32] = &[1, 2, 3, 4, 5]; + let _ = return_owned_from_slice(slice); + let _ = slice.to_owned(); + let _ = slice.to_vec(); + + let str = "hello world".to_string(); + let _ = str.clone(); + + // testing w/ an arbitrary type + let kitten = Kitten {}; + let _ = kitten.clone(); + let _ = own_same_from_ref(&kitten); + // this shouln't lint + let _ = kitten.to_vec(); + + // we expect no lints for this + let borrowed = BorrowedKitten {}; + let _ = borrowed.to_owned(); + + let pathbuf = PathBuf::new(); + let _ = pathbuf.clone(); + let _ = pathbuf.clone(); + + let os_string = OsString::from("foo"); + let _ = os_string.clone(); + let _ = os_string.clone(); + + // we expect no lints for this + let os_str = OsStr::new("foo"); + let _ = os_str.to_owned(); + let _ = os_str.to_os_string(); + + // issue #8227 + let pathbuf_ref = &pathbuf; + let pathbuf_ref = &pathbuf_ref; + let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&PathBuf` + let _ = (*pathbuf_ref).clone(); + let pathbuf_ref = &pathbuf_ref; + let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf` + let _ = (**pathbuf_ref).clone(); +} diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index 2549c9f32f9..fc896525bd2 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -1,5 +1,6 @@ +// run-rustfix #![warn(clippy::implicit_clone)] -#![allow(clippy::redundant_clone)] +#![allow(clippy::clone_on_copy, clippy::redundant_clone)] use std::borrow::Borrow; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; diff --git a/tests/ui/implicit_clone.stderr b/tests/ui/implicit_clone.stderr index 0f412424190..92c1aa58aff 100644 --- a/tests/ui/implicit_clone.stderr +++ b/tests/ui/implicit_clone.stderr @@ -1,5 +1,5 @@ error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:65:13 + --> $DIR/implicit_clone.rs:66:13 | LL | let _ = vec.to_owned(); | ^^^^^^^^^^^^^^ help: consider using: `vec.clone()` @@ -7,67 +7,67 @@ LL | let _ = vec.to_owned(); = note: `-D clippy::implicit-clone` implied by `-D warnings` error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type - --> $DIR/implicit_clone.rs:66:13 + --> $DIR/implicit_clone.rs:67:13 | LL | let _ = vec.to_vec(); | ^^^^^^^^^^^^ help: consider using: `vec.clone()` error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:70:13 + --> $DIR/implicit_clone.rs:71:13 | LL | let _ = vec_ref.to_owned(); | ^^^^^^^^^^^^^^^^^^ help: consider using: `vec_ref.clone()` error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type - --> $DIR/implicit_clone.rs:71:13 + --> $DIR/implicit_clone.rs:72:13 | LL | let _ = vec_ref.to_vec(); | ^^^^^^^^^^^^^^^^ help: consider using: `vec_ref.clone()` error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:83:13 + --> $DIR/implicit_clone.rs:84:13 | LL | let _ = str.to_owned(); | ^^^^^^^^^^^^^^ help: consider using: `str.clone()` error: implicitly cloning a `Kitten` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:87:13 + --> $DIR/implicit_clone.rs:88:13 | LL | let _ = kitten.to_owned(); | ^^^^^^^^^^^^^^^^^ help: consider using: `kitten.clone()` error: implicitly cloning a `PathBuf` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:97:13 + --> $DIR/implicit_clone.rs:98:13 | LL | let _ = pathbuf.to_owned(); | ^^^^^^^^^^^^^^^^^^ help: consider using: `pathbuf.clone()` error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type - --> $DIR/implicit_clone.rs:98:13 + --> $DIR/implicit_clone.rs:99:13 | LL | let _ = pathbuf.to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `pathbuf.clone()` error: implicitly cloning a `OsString` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:101:13 + --> $DIR/implicit_clone.rs:102:13 | LL | let _ = os_string.to_owned(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `os_string.clone()` error: implicitly cloning a `OsString` by calling `to_os_string` on its dereferenced type - --> $DIR/implicit_clone.rs:102:13 + --> $DIR/implicit_clone.rs:103:13 | LL | let _ = os_string.to_os_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `os_string.clone()` error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type - --> $DIR/implicit_clone.rs:113:13 + --> $DIR/implicit_clone.rs:114:13 | LL | let _ = pathbuf_ref.to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(*pathbuf_ref).clone()` error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type - --> $DIR/implicit_clone.rs:116:13 + --> $DIR/implicit_clone.rs:117:13 | LL | let _ = pathbuf_ref.to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(**pathbuf_ref).clone()` diff --git a/tests/ui/issue_2356.fixed b/tests/ui/issue_2356.fixed new file mode 100644 index 00000000000..942e99fa878 --- /dev/null +++ b/tests/ui/issue_2356.fixed @@ -0,0 +1,26 @@ +// run-rustfix +#![deny(clippy::while_let_on_iterator)] +#![allow(unused_mut)] + +use std::iter::Iterator; + +struct Foo; + +impl Foo { + fn foo1>(mut it: I) { + while let Some(_) = it.next() { + println!("{:?}", it.size_hint()); + } + } + + fn foo2>(mut it: I) { + for e in it { + println!("{:?}", e); + } + } +} + +fn main() { + Foo::foo1(vec![].into_iter()); + Foo::foo2(vec![].into_iter()); +} diff --git a/tests/ui/issue_2356.rs b/tests/ui/issue_2356.rs index da580a1839a..b000234ea59 100644 --- a/tests/ui/issue_2356.rs +++ b/tests/ui/issue_2356.rs @@ -1,4 +1,6 @@ +// run-rustfix #![deny(clippy::while_let_on_iterator)] +#![allow(unused_mut)] use std::iter::Iterator; diff --git a/tests/ui/issue_2356.stderr b/tests/ui/issue_2356.stderr index 51b872e21c0..4e3ff7522e0 100644 --- a/tests/ui/issue_2356.stderr +++ b/tests/ui/issue_2356.stderr @@ -1,11 +1,11 @@ error: this loop could be written as a `for` loop - --> $DIR/issue_2356.rs:15:9 + --> $DIR/issue_2356.rs:17:9 | LL | while let Some(e) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it` | note: the lint level is defined here - --> $DIR/issue_2356.rs:1:9 + --> $DIR/issue_2356.rs:2:9 | LL | #![deny(clippy::while_let_on_iterator)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/iter_next_slice.fixed b/tests/ui/iter_next_slice.fixed index 11ffc8edb14..f612d26aaab 100644 --- a/tests/ui/iter_next_slice.fixed +++ b/tests/ui/iter_next_slice.fixed @@ -6,8 +6,8 @@ fn main() { let s = [1, 2, 3]; let v = vec![1, 2, 3]; - let _ = s.get(0); - // Should be replaced by s.get(0) + let _ = s.first(); + // Should be replaced by s.first() let _ = s.get(2); // Should be replaced by s.get(2) @@ -15,8 +15,8 @@ fn main() { let _ = v.get(5); // Should be replaced by v.get(5) - let _ = v.get(0); - // Should be replaced by v.get(0) + let _ = v.first(); + // Should be replaced by v.first() let o = Some(5); o.iter().next(); diff --git a/tests/ui/iter_next_slice.rs b/tests/ui/iter_next_slice.rs index e0d3aabd54a..5195f1c8667 100644 --- a/tests/ui/iter_next_slice.rs +++ b/tests/ui/iter_next_slice.rs @@ -7,7 +7,7 @@ fn main() { let v = vec![1, 2, 3]; let _ = s.iter().next(); - // Should be replaced by s.get(0) + // Should be replaced by s.first() let _ = s[2..].iter().next(); // Should be replaced by s.get(2) @@ -16,7 +16,7 @@ fn main() { // Should be replaced by v.get(5) let _ = v.iter().next(); - // Should be replaced by v.get(0) + // Should be replaced by v.first() let o = Some(5); o.iter().next(); diff --git a/tests/ui/iter_next_slice.stderr b/tests/ui/iter_next_slice.stderr index a78d2c2d5e8..d8b89061ff8 100644 --- a/tests/ui/iter_next_slice.stderr +++ b/tests/ui/iter_next_slice.stderr @@ -2,7 +2,7 @@ error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:9:13 | LL | let _ = s.iter().next(); - | ^^^^^^^^^^^^^^^ help: try calling: `s.get(0)` + | ^^^^^^^^^^^^^^^ help: try calling: `s.first()` | = note: `-D clippy::iter-next-slice` implied by `-D warnings` @@ -22,7 +22,7 @@ error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:18:13 | LL | let _ = v.iter().next(); - | ^^^^^^^^^^^^^^^ help: try calling: `v.get(0)` + | ^^^^^^^^^^^^^^^ help: try calling: `v.first()` error: aborting due to 4 previous errors diff --git a/tests/ui/large_enum_variant.rs b/tests/ui/large_enum_variant.rs index cee9e2372c2..23152a13322 100644 --- a/tests/ui/large_enum_variant.rs +++ b/tests/ui/large_enum_variant.rs @@ -98,6 +98,38 @@ struct Struct2 { a: [i32; 8000], } +#[derive(Copy, Clone)] +enum CopyableLargeEnum { + A(bool), + B([u128; 4000]), +} + +enum ManuallyCopyLargeEnum { + A(bool), + B([u128; 4000]), +} + +impl Clone for ManuallyCopyLargeEnum { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for ManuallyCopyLargeEnum {} + +enum SomeGenericPossiblyCopyEnum { + A(bool, std::marker::PhantomData), + B([u64; 4000]), +} + +impl Clone for SomeGenericPossiblyCopyEnum { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for SomeGenericPossiblyCopyEnum {} + fn main() { large_enum_variant!(); } diff --git a/tests/ui/large_enum_variant.stderr b/tests/ui/large_enum_variant.stderr index cbf2ac972e2..0248327262d 100644 --- a/tests/ui/large_enum_variant.stderr +++ b/tests/ui/large_enum_variant.stderr @@ -127,5 +127,71 @@ help: consider boxing the large fields to reduce the total size of the enum LL | B(Box), | ~~~~~~~~~~~~ -error: aborting due to 8 previous errors +error: large size difference between variants + --> $DIR/large_enum_variant.rs:104:5 + | +LL | B([u128; 4000]), + | ^^^^^^^^^^^^^^^ this variant is 64000 bytes + | +note: and the second-largest variant is 1 bytes: + --> $DIR/large_enum_variant.rs:103:5 + | +LL | A(bool), + | ^^^^^^^ +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:102:6 + | +LL | enum CopyableLargeEnum { + | ^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:104:5 + | +LL | B([u128; 4000]), + | ^^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:109:5 + | +LL | B([u128; 4000]), + | ^^^^^^^^^^^^^^^ this variant is 64000 bytes + | +note: and the second-largest variant is 1 bytes: + --> $DIR/large_enum_variant.rs:108:5 + | +LL | A(bool), + | ^^^^^^^ +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:107:6 + | +LL | enum ManuallyCopyLargeEnum { + | ^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:109:5 + | +LL | B([u128; 4000]), + | ^^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:122:5 + | +LL | B([u64; 4000]), + | ^^^^^^^^^^^^^^ this variant is 32000 bytes + | +note: and the second-largest variant is 1 bytes: + --> $DIR/large_enum_variant.rs:121:5 + | +LL | A(bool, std::marker::PhantomData), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:120:6 + | +LL | enum SomeGenericPossiblyCopyEnum { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:122:5 + | +LL | B([u64; 4000]), + | ^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/map_flatten_fixable.fixed b/tests/ui/map_flatten_fixable.fixed index fec3a95edd6..928e5bd509c 100644 --- a/tests/ui/map_flatten_fixable.fixed +++ b/tests/ui/map_flatten_fixable.fixed @@ -28,4 +28,41 @@ fn main() { // mapping to Result on Result let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x); + + issue8734(); + issue8878(); +} + +fn issue8734() { + // let _ = [0u8, 1, 2, 3] + // .into_iter() + // .map(|n| match n { + // 1 => [n + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1)], + // n => [n], + // }) + // .flatten(); +} + +#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again +#[rustfmt::skip] // whitespace is important for this one +fn issue8878() { + std::collections::HashMap::::new() + .get(&0) + .and_then(|_| { +// we need some newlines +// so that the span is big enough +// we need some newlines +// so that the span is big enough +// for a splitted output of the diagnostic + Some("") + // whitespace beforehand is important as well + }); } diff --git a/tests/ui/map_flatten_fixable.rs b/tests/ui/map_flatten_fixable.rs index aa1f76e335a..4345c6eee74 100644 --- a/tests/ui/map_flatten_fixable.rs +++ b/tests/ui/map_flatten_fixable.rs @@ -28,4 +28,40 @@ fn main() { // mapping to Result on Result let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); + + issue8734(); + issue8878(); +} + +fn issue8734() { + // let _ = [0u8, 1, 2, 3] + // .into_iter() + // .map(|n| match n { + // 1 => [n + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1)], + // n => [n], + // }) + // .flatten(); +} + +#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again +#[rustfmt::skip] // whitespace is important for this one +fn issue8878() { + std::collections::HashMap::::new() + .get(&0) + .map(|_| { +// we need some newlines +// so that the span is big enough +// for a splitted output of the diagnostic + Some("") + // whitespace beforehand is important as well + }) + .flatten(); } diff --git a/tests/ui/map_flatten_fixable.stderr b/tests/ui/map_flatten_fixable.stderr index c91c73846b6..828e24acaad 100644 --- a/tests/ui/map_flatten_fixable.stderr +++ b/tests/ui/map_flatten_fixable.stderr @@ -76,5 +76,31 @@ help: try replacing `map` with `and_then`, and remove the `.flatten()` LL | let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x); | ~~~~~~~~~~~~~~~ -error: aborting due to 7 previous errors +error: called `map(..).flatten()` on `Option` + --> $DIR/map_flatten_fixable.rs:59:10 + | +LL | .map(|_| { + | __________^ +LL | | // we need some newlines +LL | | // so that the span is big enough +LL | | // for a splitted output of the diagnostic +... | +LL | | }) +LL | | .flatten(); + | |__________________^ + | +help: try replacing `map` with `and_then` + | +LL ~ .and_then(|_| { +LL + // we need some newlines +LL + // so that the span is big enough + | +help: and remove the `.flatten()` + | +LL + Some("") +LL + // whitespace beforehand is important as well +LL ~ }); + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/match_ref_pats.fixed b/tests/ui/match_ref_pats.fixed new file mode 100644 index 00000000000..1b6c2d92412 --- /dev/null +++ b/tests/ui/match_ref_pats.fixed @@ -0,0 +1,118 @@ +// run-rustfix +#![warn(clippy::match_ref_pats)] +#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)] + +fn ref_pats() { + { + let v = &Some(0); + match *v { + Some(v) => println!("{:?}", v), + None => println!("none"), + } + match v { + // This doesn't trigger; we have a different pattern. + &Some(v) => println!("some"), + other => println!("other"), + } + } + let tup = &(1, 2); + match tup { + &(v, 1) => println!("{}", v), + _ => println!("none"), + } + // Special case: using `&` both in expr and pats. + let w = Some(0); + match w { + Some(v) => println!("{:?}", v), + None => println!("none"), + } + // False positive: only wildcard pattern. + let w = Some(0); + #[allow(clippy::match_single_binding)] + match w { + _ => println!("none"), + } + + let a = &Some(0); + if a.is_none() { + println!("none"); + } + + let b = Some(0); + if b.is_none() { + println!("none"); + } +} + +mod ice_3719 { + macro_rules! foo_variant( + ($idx:expr) => (Foo::get($idx).unwrap()) + ); + + enum Foo { + A, + B, + } + + impl Foo { + fn get(idx: u8) -> Option<&'static Self> { + match idx { + 0 => Some(&Foo::A), + 1 => Some(&Foo::B), + _ => None, + } + } + } + + fn ice_3719() { + // ICE #3719 + match foo_variant!(0) { + &Foo::A => println!("A"), + _ => println!("Wild"), + } + } +} + +mod issue_7740 { + macro_rules! foobar_variant( + ($idx:expr) => (FooBar::get($idx).unwrap()) + ); + + enum FooBar { + Foo, + Bar, + FooBar, + BarFoo, + } + + impl FooBar { + fn get(idx: u8) -> Option<&'static Self> { + match idx { + 0 => Some(&FooBar::Foo), + 1 => Some(&FooBar::Bar), + 2 => Some(&FooBar::FooBar), + 3 => Some(&FooBar::BarFoo), + _ => None, + } + } + } + + fn issue_7740() { + // Issue #7740 + match *foobar_variant!(0) { + FooBar::Foo => println!("Foo"), + FooBar::Bar => println!("Bar"), + FooBar::FooBar => println!("FooBar"), + _ => println!("Wild"), + } + + // This shouldn't trigger + if let &FooBar::BarFoo = foobar_variant!(3) { + println!("BarFoo"); + } else { + println!("Wild"); + } + } +} + +fn main() {} diff --git a/tests/ui/match_ref_pats.rs b/tests/ui/match_ref_pats.rs index 7e3674ab8c9..68dfac4e2e9 100644 --- a/tests/ui/match_ref_pats.rs +++ b/tests/ui/match_ref_pats.rs @@ -1,5 +1,6 @@ +// run-rustfix #![warn(clippy::match_ref_pats)] -#![allow(clippy::equatable_if_let, clippy::enum_variant_names)] +#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)] fn ref_pats() { { diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index 901820077e2..353f7399d9c 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -1,5 +1,5 @@ error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:7:9 + --> $DIR/match_ref_pats.rs:8:9 | LL | / match v { LL | | &Some(v) => println!("{:?}", v), @@ -16,7 +16,7 @@ LL ~ None => println!("none"), | error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_ref_pats.rs:24:5 + --> $DIR/match_ref_pats.rs:25:5 | LL | / match &w { LL | | &Some(v) => println!("{:?}", v), @@ -32,7 +32,7 @@ LL ~ None => println!("none"), | error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:36:12 + --> $DIR/match_ref_pats.rs:37:12 | LL | if let &None = a { | -------^^^^^---- help: try this: `if a.is_none()` @@ -40,13 +40,13 @@ LL | if let &None = a { = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:41:12 + --> $DIR/match_ref_pats.rs:42:12 | LL | if let &None = &b { | -------^^^^^----- help: try this: `if b.is_none()` error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:101:9 + --> $DIR/match_ref_pats.rs:102:9 | LL | / match foobar_variant!(0) { LL | | &FooBar::Foo => println!("Foo"), diff --git a/tests/ui/match_str_case_mismatch.fixed b/tests/ui/match_str_case_mismatch.fixed new file mode 100644 index 00000000000..e436bcf495f --- /dev/null +++ b/tests/ui/match_str_case_mismatch.fixed @@ -0,0 +1,186 @@ +// run-rustfix +#![warn(clippy::match_str_case_mismatch)] +#![allow(dead_code)] + +// Valid + +fn as_str_match() { + let var = "BAR"; + + match var.to_ascii_lowercase().as_str() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn non_alphabetic() { + let var = "~!@#$%^&*()-_=+FOO"; + + match var.to_ascii_lowercase().as_str() { + "1234567890" => {}, + "~!@#$%^&*()-_=+foo" => {}, + "\n\r\t\x7F" => {}, + _ => {}, + } +} + +fn unicode_cased() { + let var = "ВОДЫ"; + + match var.to_lowercase().as_str() { + "水" => {}, + "νερό" => {}, + "воды" => {}, + "물" => {}, + _ => {}, + } +} + +fn titlecase() { + let var = "BarDz"; + + match var.to_lowercase().as_str() { + "foolj" => {}, + "bardz" => {}, + _ => {}, + } +} + +fn no_case_equivalent() { + let var = "barʁ"; + + match var.to_uppercase().as_str() { + "FOOɕ" => {}, + "BARʁ" => {}, + _ => {}, + } +} + +fn addrof_unary_match() { + let var = "BAR"; + + match &*var.to_ascii_lowercase() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn alternating_chain() { + let var = "BAR"; + + match &*var + .to_ascii_lowercase() + .to_uppercase() + .to_lowercase() + .to_ascii_uppercase() + { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +fn unrelated_method() { + struct Item { + a: String, + } + + impl Item { + #[allow(clippy::wrong_self_convention)] + fn to_lowercase(self) -> String { + self.a + } + } + + let item = Item { a: String::from("BAR") }; + + match &*item.to_lowercase() { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +// Invalid + +fn as_str_match_mismatch() { + let var = "BAR"; + + match var.to_ascii_lowercase().as_str() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn non_alphabetic_mismatch() { + let var = "~!@#$%^&*()-_=+FOO"; + + match var.to_ascii_lowercase().as_str() { + "1234567890" => {}, + "~!@#$%^&*()-_=+foo" => {}, + "\n\r\t\x7F" => {}, + _ => {}, + } +} + +fn unicode_cased_mismatch() { + let var = "ВОДЫ"; + + match var.to_lowercase().as_str() { + "水" => {}, + "νερό" => {}, + "воды" => {}, + "물" => {}, + _ => {}, + } +} + +fn titlecase_mismatch() { + let var = "BarDz"; + + match var.to_lowercase().as_str() { + "foolj" => {}, + "bardz" => {}, + _ => {}, + } +} + +fn no_case_equivalent_mismatch() { + let var = "barʁ"; + + match var.to_uppercase().as_str() { + "FOOɕ" => {}, + "BARʁ" => {}, + _ => {}, + } +} + +fn addrof_unary_match_mismatch() { + let var = "BAR"; + + match &*var.to_ascii_lowercase() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn alternating_chain_mismatch() { + let var = "BAR"; + + match &*var + .to_ascii_lowercase() + .to_uppercase() + .to_lowercase() + .to_ascii_uppercase() + { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +fn main() {} diff --git a/tests/ui/match_str_case_mismatch.rs b/tests/ui/match_str_case_mismatch.rs index ac555c87d83..92e2a000ade 100644 --- a/tests/ui/match_str_case_mismatch.rs +++ b/tests/ui/match_str_case_mismatch.rs @@ -1,4 +1,6 @@ +// run-rustfix #![warn(clippy::match_str_case_mismatch)] +#![allow(dead_code)] // Valid diff --git a/tests/ui/match_str_case_mismatch.stderr b/tests/ui/match_str_case_mismatch.stderr index 92baa40ef28..197520a3d60 100644 --- a/tests/ui/match_str_case_mismatch.stderr +++ b/tests/ui/match_str_case_mismatch.stderr @@ -1,5 +1,5 @@ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:111:9 + --> $DIR/match_str_case_mismatch.rs:113:9 | LL | "Bar" => {}, | ^^^^^ @@ -11,7 +11,7 @@ LL | "bar" => {}, | ~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:121:9 + --> $DIR/match_str_case_mismatch.rs:123:9 | LL | "~!@#$%^&*()-_=+Foo" => {}, | ^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | "~!@#$%^&*()-_=+foo" => {}, | ~~~~~~~~~~~~~~~~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:133:9 + --> $DIR/match_str_case_mismatch.rs:135:9 | LL | "Воды" => {}, | ^^^^^^ @@ -33,7 +33,7 @@ LL | "воды" => {}, | ~~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:144:9 + --> $DIR/match_str_case_mismatch.rs:146:9 | LL | "barDz" => {}, | ^^^^^^ @@ -44,7 +44,7 @@ LL | "bardz" => {}, | ~~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:154:9 + --> $DIR/match_str_case_mismatch.rs:156:9 | LL | "bARʁ" => {}, | ^^^^^^ @@ -55,7 +55,7 @@ LL | "BARʁ" => {}, | ~~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:164:9 + --> $DIR/match_str_case_mismatch.rs:166:9 | LL | "Bar" => {}, | ^^^^^ @@ -66,7 +66,7 @@ LL | "bar" => {}, | ~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:179:9 + --> $DIR/match_str_case_mismatch.rs:181:9 | LL | "bAR" => {}, | ^^^^^ diff --git a/tests/ui/mismatching_type_param_order.rs b/tests/ui/mismatching_type_param_order.rs new file mode 100644 index 00000000000..8f286c9304c --- /dev/null +++ b/tests/ui/mismatching_type_param_order.rs @@ -0,0 +1,60 @@ +#![warn(clippy::mismatching_type_param_order)] +#![allow(clippy::blacklisted_name)] + +fn main() { + struct Foo { + x: A, + y: B, + } + + // lint on both params + impl Foo {} + + // lint on the 2nd param + impl Foo {} + + // should not lint + impl Foo {} + + struct FooLifetime<'l, 'm, A, B> { + x: &'l A, + y: &'m B, + } + + // should not lint on lifetimes + impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} + + struct Bar { + x: i32, + } + + // should not lint + impl Bar {} + + // also works for enums + enum FooEnum { + X(A), + Y(B), + Z(C), + } + + impl FooEnum {} + + // also works for unions + union FooUnion + where + B: Copy, + { + x: A, + y: B, + } + + impl FooUnion where A: Copy {} + + impl FooUnion + where + A: Copy, + B: Copy, + { + } +} diff --git a/tests/ui/mismatching_type_param_order.stderr b/tests/ui/mismatching_type_param_order.stderr new file mode 100644 index 00000000000..cb720256c50 --- /dev/null +++ b/tests/ui/mismatching_type_param_order.stderr @@ -0,0 +1,83 @@ +error: `Foo` has a similarly named generic type parameter `B` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:11:20 + | +LL | impl Foo {} + | ^ + | + = note: `-D clippy::mismatching-type-param-order` implied by `-D warnings` + = help: try `A`, or a name that does not conflict with `Foo`'s generic params + +error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:11:23 + | +LL | impl Foo {} + | ^ + | + = help: try `B`, or a name that does not conflict with `Foo`'s generic params + +error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:14:23 + | +LL | impl Foo {} + | ^ + | + = help: try `B`, or a name that does not conflict with `Foo`'s generic params + +error: `FooLifetime` has a similarly named generic type parameter `B` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:25:44 + | +LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} + | ^ + | + = help: try `A`, or a name that does not conflict with `FooLifetime`'s generic params + +error: `FooLifetime` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:25:47 + | +LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} + | ^ + | + = help: try `B`, or a name that does not conflict with `FooLifetime`'s generic params + +error: `FooEnum` has a similarly named generic type parameter `C` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:41:27 + | +LL | impl FooEnum {} + | ^ + | + = help: try `A`, or a name that does not conflict with `FooEnum`'s generic params + +error: `FooEnum` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:41:30 + | +LL | impl FooEnum {} + | ^ + | + = help: try `B`, or a name that does not conflict with `FooEnum`'s generic params + +error: `FooEnum` has a similarly named generic type parameter `B` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:41:33 + | +LL | impl FooEnum {} + | ^ + | + = help: try `C`, or a name that does not conflict with `FooEnum`'s generic params + +error: `FooUnion` has a similarly named generic type parameter `B` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:52:31 + | +LL | impl FooUnion where A: Copy {} + | ^ + | + = help: try `A`, or a name that does not conflict with `FooUnion`'s generic params + +error: `FooUnion` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:52:34 + | +LL | impl FooUnion where A: Copy {} + | ^ + | + = help: try `B`, or a name that does not conflict with `FooUnion`'s generic params + +error: aborting due to 10 previous errors + diff --git a/tests/ui/modulo_one.rs b/tests/ui/modulo_one.rs index 678a312f66e..adff08e5d1e 100644 --- a/tests/ui/modulo_one.rs +++ b/tests/ui/modulo_one.rs @@ -1,5 +1,5 @@ #![warn(clippy::modulo_one)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::identity_op)] static STATIC_ONE: usize = 2 - 1; static STATIC_NEG_ONE: i64 = 1 - 2; diff --git a/tests/ui/modulo_one.stderr b/tests/ui/modulo_one.stderr index 03f460897fc..04ecdef5e99 100644 --- a/tests/ui/modulo_one.stderr +++ b/tests/ui/modulo_one.stderr @@ -38,14 +38,6 @@ error: any number modulo -1 will panic/overflow or result in 0 LL | i32::MIN % (-1); // also caught by rustc | ^^^^^^^^^^^^^^^ -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/modulo_one.rs:13:22 - | -LL | const ONE: u32 = 1 * 1; - | ^^^^^ - | - = note: `-D clippy::identity-op` implied by `-D warnings` - error: any number modulo 1 will be 0 --> $DIR/modulo_one.rs:17:5 | @@ -64,5 +56,5 @@ error: any number modulo -1 will panic/overflow or result in 0 LL | INT_MIN % NEG_ONE; // also caught by rustc | ^^^^^^^^^^^^^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed new file mode 100644 index 00000000000..fee8e3030b8 --- /dev/null +++ b/tests/ui/needless_late_init.fixed @@ -0,0 +1,273 @@ +// run-rustfix +#![feature(let_chains)] +#![allow( + unused, + clippy::assign_op_pattern, + clippy::blocks_in_if_conditions, + clippy::let_and_return, + clippy::let_unit_value, + clippy::nonminimal_bool +)] + +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::rc::Rc; + +struct SignificantDrop; +impl std::ops::Drop for SignificantDrop { + fn drop(&mut self) { + println!("dropped"); + } +} + +fn simple() { + + let a = "zero"; + + + + let b = 1; + let c = 2; + + + let d: usize = 1; + + + let e = format!("{}", d); +} + +fn main() { + + let n = 1; + let a = match n { + 1 => "one", + _ => { + "two" + }, + }; + + + let b = if n == 3 { + "four" + } else { + "five" + }; + + + let d = if true { + let temp = 5; + temp + } else { + 15 + }; + + + let e = if true { + format!("{} {}", a, b) + } else { + format!("{}", n) + }; + + + let f = match 1 { + 1 => "three", + _ => return, + }; // has semi + + + let g: usize = if true { + 5 + } else { + panic!(); + }; + + // Drop order only matters if both are significant + + let y = SignificantDrop; + let x = 1; + + + let y = 1; + let x = SignificantDrop; + + + // types that should be considered insignificant + let y = 1; + let y = "2"; + let y = String::new(); + let y = vec![3.0]; + let y = HashMap::::new(); + let y = BTreeMap::::new(); + let y = HashSet::::new(); + let y = BTreeSet::::new(); + let y = Box::new(4); + let x = SignificantDrop; +} + +async fn in_async() -> &'static str { + async fn f() -> &'static str { + "one" + } + + + let n = 1; + let a = match n { + 1 => f().await, + _ => { + "two" + }, + }; + + a +} + +const fn in_const() -> &'static str { + const fn f() -> &'static str { + "one" + } + + + let n = 1; + let a = match n { + 1 => f(), + _ => { + "two" + }, + }; + + a +} + +fn does_not_lint() { + let z; + if false { + z = 1; + } + + let x; + let y; + if true { + x = 1; + } else { + y = 1; + } + + let mut x; + if true { + x = 5; + x = 10 / x; + } else { + x = 2; + } + + let x; + let _ = match 1 { + 1 => x = 10, + _ => x = 20, + }; + + // using tuples would be possible, but not always preferable + let x; + let y; + if true { + x = 1; + y = 2; + } else { + x = 3; + y = 4; + } + + // could match with a smarter heuristic to avoid multiple assignments + let x; + if true { + let mut y = 5; + y = 6; + x = y; + } else { + x = 2; + } + + let (x, y); + if true { + x = 1; + } else { + x = 2; + } + y = 3; + + macro_rules! assign { + ($i:ident) => { + $i = 1; + }; + } + let x; + assign!(x); + + let x; + if true { + assign!(x); + } else { + x = 2; + } + + macro_rules! in_macro { + () => { + let x; + x = 1; + + let x; + if true { + x = 1; + } else { + x = 2; + } + }; + } + in_macro!(); + + // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613 + let x; + if let Some(n) = Some("v") { + x = 1; + } else { + x = 2; + } + + let x; + if true && let Some(n) = Some("let chains too") { + x = 1; + } else { + x = 2; + } + + // ignore mut bindings + // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93 + // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100 + let mut x: usize; + x = 1; + x = 2; + x = 3; + + // should not move the declaration if `x` has a significant drop, and there + // is another binding with a significant drop between it and the first usage + let x; + let y = SignificantDrop; + x = SignificantDrop; +} + +#[rustfmt::skip] +fn issue8911() -> u32 { + let x; + match 1 { + _ if { x = 1; false } => return 1, + _ => return 2, + } + + let x; + if { x = 1; true } { + return 1; + } else { + return 2; + } + + 3 +} diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 54e66b391b8..402d9f9ef7f 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,5 +1,13 @@ +// run-rustfix #![feature(let_chains)] -#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value)] +#![allow( + unused, + clippy::assign_op_pattern, + clippy::blocks_in_if_conditions, + clippy::let_and_return, + clippy::let_unit_value, + clippy::nonminimal_bool +)] use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::rc::Rc; @@ -11,6 +19,22 @@ impl std::ops::Drop for SignificantDrop { } } +fn simple() { + let a; + a = "zero"; + + let b; + let c; + b = 1; + c = 2; + + let d: usize; + d = 1; + + let e; + e = format!("{}", d); +} + fn main() { let a; let n = 1; @@ -229,3 +253,21 @@ fn does_not_lint() { let y = SignificantDrop; x = SignificantDrop; } + +#[rustfmt::skip] +fn issue8911() -> u32 { + let x; + match 1 { + _ if { x = 1; false } => return 1, + _ => return 2, + } + + let x; + if { x = 1; true } { + return 1; + } else { + return 2; + } + + 3 +} diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index d33a117b288..f320b5b9cbb 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,10 +1,77 @@ error: unneeded late initialization - --> $DIR/needless_late_init.rs:15:5 + --> $DIR/needless_late_init.rs:23:5 + | +LL | let a; + | ^^^^^^ created here +LL | a = "zero"; + | ^^^^^^^^^^ initialised here + | + = note: `-D clippy::needless-late-init` implied by `-D warnings` +help: declare `a` here + | +LL | let a = "zero"; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:26:5 + | +LL | let b; + | ^^^^^^ created here +LL | let c; +LL | b = 1; + | ^^^^^ initialised here + | +help: declare `b` here + | +LL | let b = 1; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:27:5 + | +LL | let c; + | ^^^^^^ created here +LL | b = 1; +LL | c = 2; + | ^^^^^ initialised here + | +help: declare `c` here + | +LL | let c = 2; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:31:5 + | +LL | let d: usize; + | ^^^^^^^^^^^^^ created here +LL | d = 1; + | ^^^^^ initialised here + | +help: declare `d` here + | +LL | let d: usize = 1; + | ~~~~~~~~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:34:5 + | +LL | let e; + | ^^^^^^ created here +LL | e = format!("{}", d); + | ^^^^^^^^^^^^^^^^^^^^ initialised here + | +help: declare `e` here + | +LL | let e = format!("{}", d); + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:39:5 | LL | let a; | ^^^^^^ | - = note: `-D clippy::needless-late-init` implied by `-D warnings` help: declare `a` here | LL | let a = match n { @@ -21,7 +88,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:24:5 + --> $DIR/needless_late_init.rs:48:5 | LL | let b; | ^^^^^^ @@ -42,7 +109,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:31:5 + --> $DIR/needless_late_init.rs:55:5 | LL | let d; | ^^^^^^ @@ -63,7 +130,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:39:5 + --> $DIR/needless_late_init.rs:63:5 | LL | let e; | ^^^^^^ @@ -84,7 +151,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:46:5 + --> $DIR/needless_late_init.rs:70:5 | LL | let f; | ^^^^^^ @@ -100,7 +167,7 @@ LL + 1 => "three", | error: unneeded late initialization - --> $DIR/needless_late_init.rs:52:5 + --> $DIR/needless_late_init.rs:76:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -120,7 +187,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:60:5 + --> $DIR/needless_late_init.rs:84:5 | LL | let x; | ^^^^^^ created here @@ -134,7 +201,7 @@ LL | let x = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:64:5 + --> $DIR/needless_late_init.rs:88:5 | LL | let x; | ^^^^^^ created here @@ -148,7 +215,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:68:5 + --> $DIR/needless_late_init.rs:92:5 | LL | let x; | ^^^^^^ created here @@ -162,7 +229,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:87:5 + --> $DIR/needless_late_init.rs:111:5 | LL | let a; | ^^^^^^ @@ -183,7 +250,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:104:5 + --> $DIR/needless_late_init.rs:128:5 | LL | let a; | ^^^^^^ @@ -203,5 +270,5 @@ help: add a semicolon after the `match` expression LL | }; | + -error: aborting due to 11 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/needless_late_init_fixable.fixed b/tests/ui/needless_late_init_fixable.fixed deleted file mode 100644 index 724477e8691..00000000000 --- a/tests/ui/needless_late_init_fixable.fixed +++ /dev/null @@ -1,19 +0,0 @@ -// run-rustfix - -#![allow(unused, clippy::assign_op_pattern)] - -fn main() { - - let a = "zero"; - - - - let b = 1; - let c = 2; - - - let d: usize = 1; - - - let e = format!("{}", d); -} diff --git a/tests/ui/needless_late_init_fixable.rs b/tests/ui/needless_late_init_fixable.rs deleted file mode 100644 index 3e6bd363672..00000000000 --- a/tests/ui/needless_late_init_fixable.rs +++ /dev/null @@ -1,19 +0,0 @@ -// run-rustfix - -#![allow(unused, clippy::assign_op_pattern)] - -fn main() { - let a; - a = "zero"; - - let b; - let c; - b = 1; - c = 2; - - let d: usize; - d = 1; - - let e; - e = format!("{}", d); -} diff --git a/tests/ui/needless_late_init_fixable.stderr b/tests/ui/needless_late_init_fixable.stderr deleted file mode 100644 index 8c664309e3e..00000000000 --- a/tests/ui/needless_late_init_fixable.stderr +++ /dev/null @@ -1,70 +0,0 @@ -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:6:5 - | -LL | let a; - | ^^^^^^ created here -LL | a = "zero"; - | ^^^^^^^^^^ initialised here - | - = note: `-D clippy::needless-late-init` implied by `-D warnings` -help: declare `a` here - | -LL | let a = "zero"; - | ~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:9:5 - | -LL | let b; - | ^^^^^^ created here -LL | let c; -LL | b = 1; - | ^^^^^ initialised here - | -help: declare `b` here - | -LL | let b = 1; - | ~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:10:5 - | -LL | let c; - | ^^^^^^ created here -LL | b = 1; -LL | c = 2; - | ^^^^^ initialised here - | -help: declare `c` here - | -LL | let c = 2; - | ~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:14:5 - | -LL | let d: usize; - | ^^^^^^^^^^^^^ created here -LL | d = 1; - | ^^^^^ initialised here - | -help: declare `d` here - | -LL | let d: usize = 1; - | ~~~~~~~~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:17:5 - | -LL | let e; - | ^^^^^^ created here -LL | e = format!("{}", d); - | ^^^^^^^^^^^^^^^^^^^^ initialised here - | -help: declare `e` here - | -LL | let e = format!("{}", d); - | ~~~~~ - -error: aborting due to 5 previous errors - diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 1456204ca86..fc686b1dac0 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -4,7 +4,8 @@ clippy::boxed_local, clippy::needless_pass_by_value, clippy::unnecessary_wraps, - dyn_drop + dyn_drop, + clippy::get_first )] fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index a488bc01fff..3c428fd4674 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -1,5 +1,5 @@ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:10:1 + --> $DIR/needless_lifetimes.rs:11:1 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,181 +7,181 @@ LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} = note: `-D clippy::needless-lifetimes` implied by `-D warnings` error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:12:1 + --> $DIR/needless_lifetimes.rs:13:1 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:22:1 + --> $DIR/needless_lifetimes.rs:23:1 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:56:1 + --> $DIR/needless_lifetimes.rs:57:1 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:61:1 + --> $DIR/needless_lifetimes.rs:62:1 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:73:1 + --> $DIR/needless_lifetimes.rs:74:1 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:97:1 + --> $DIR/needless_lifetimes.rs:98:1 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:127:5 + --> $DIR/needless_lifetimes.rs:128:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:136:5 + --> $DIR/needless_lifetimes.rs:137:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:155:1 + --> $DIR/needless_lifetimes.rs:156:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:185:1 + --> $DIR/needless_lifetimes.rs:186:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:191:1 + --> $DIR/needless_lifetimes.rs:192:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:210:1 + --> $DIR/needless_lifetimes.rs:211:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:218:1 + --> $DIR/needless_lifetimes.rs:219:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:254:1 + --> $DIR/needless_lifetimes.rs:255:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:261:9 + --> $DIR/needless_lifetimes.rs:262:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:265:9 + --> $DIR/needless_lifetimes.rs:266:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:278:9 + --> $DIR/needless_lifetimes.rs:279:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:310:5 + --> $DIR/needless_lifetimes.rs:311:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:319:5 + --> $DIR/needless_lifetimes.rs:320:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:331:5 + --> $DIR/needless_lifetimes.rs:332:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:346:5 + --> $DIR/needless_lifetimes.rs:347:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:359:5 + --> $DIR/needless_lifetimes.rs:360:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:362:5 + --> $DIR/needless_lifetimes.rs:363:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:384:9 + --> $DIR/needless_lifetimes.rs:385:9 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:387:9 + --> $DIR/needless_lifetimes.rs:388:9 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:398:9 + --> $DIR/needless_lifetimes.rs:399:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:404:9 + --> $DIR/needless_lifetimes.rs:405:9 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:405:9 + --> $DIR/needless_lifetimes.rs:406:9 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:414:9 + --> $DIR/needless_lifetimes.rs:415:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:415:9 + --> $DIR/needless_lifetimes.rs:416:9 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 603d438d558..7c828430b78 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -53,7 +53,7 @@ fn test_closure() { } fn test_macro_call() -> i32 { - return the_answer!(); + the_answer!() } fn test_void_fun() { @@ -175,7 +175,7 @@ async fn async_test_closure() { } async fn async_test_macro_call() -> i32 { - return the_answer!(); + the_answer!() } async fn async_test_void_fun() { @@ -223,4 +223,10 @@ fn let_else() { let Some(1) = Some(1) else { return }; } +fn needless_return_macro() -> String { + let _ = "foo"; + let _ = "bar"; + format!("Hello {}", "world!") +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index c6c8cb9ec15..fe82af00e67 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -223,4 +223,10 @@ fn let_else() { let Some(1) = Some(1) else { return }; } +fn needless_return_macro() -> String { + let _ = "foo"; + let _ = "bar"; + return format!("Hello {}", "world!"); +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 5bc787c56a6..4c8be47b025 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -48,6 +48,12 @@ error: unneeded `return` statement LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` +error: unneeded `return` statement + --> $DIR/needless_return.rs:56:5 + | +LL | return the_answer!(); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()` + error: unneeded `return` statement --> $DIR/needless_return.rs:60:5 | @@ -168,6 +174,12 @@ error: unneeded `return` statement LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` +error: unneeded `return` statement + --> $DIR/needless_return.rs:178:5 + | +LL | return the_answer!(); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()` + error: unneeded `return` statement --> $DIR/needless_return.rs:182:5 | @@ -204,5 +216,11 @@ error: unneeded `return` statement LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` -error: aborting due to 34 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:229:5 + | +LL | return format!("Hello {}", "world!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `format!("Hello {}", "world!")` + +error: aborting due to 37 previous errors diff --git a/tests/ui/no_effect_replace.rs b/tests/ui/no_effect_replace.rs new file mode 100644 index 00000000000..ad17d53f789 --- /dev/null +++ b/tests/ui/no_effect_replace.rs @@ -0,0 +1,51 @@ +#![warn(clippy::no_effect_replace)] + +fn main() { + let _ = "12345".replace('1', "1"); + let _ = "12345".replace("12", "12"); + let _ = String::new().replace("12", "12"); + + let _ = "12345".replacen('1', "1", 1); + let _ = "12345".replacen("12", "12", 1); + let _ = String::new().replacen("12", "12", 1); + + let _ = "12345".replace("12", "22"); + let _ = "12345".replacen("12", "22", 1); + + let mut x = X::default(); + let _ = "hello".replace(&x.f(), &x.f()); + let _ = "hello".replace(&x.f(), &x.ff()); + + let _ = "hello".replace(&y(), &y()); + let _ = "hello".replace(&y(), &z()); + + let _ = Replaceme.replace("a", "a"); +} + +#[derive(Default)] +struct X {} + +impl X { + fn f(&mut self) -> String { + "he".to_string() + } + + fn ff(&mut self) -> String { + "hh".to_string() + } +} + +fn y() -> String { + "he".to_string() +} + +fn z() -> String { + "hh".to_string() +} + +struct Replaceme; +impl Replaceme { + pub fn replace(&mut self, a: &str, b: &str) -> Self { + Self + } +} diff --git a/tests/ui/no_effect_replace.stderr b/tests/ui/no_effect_replace.stderr new file mode 100644 index 00000000000..53a28aa73b7 --- /dev/null +++ b/tests/ui/no_effect_replace.stderr @@ -0,0 +1,52 @@ +error: replacing text with itself + --> $DIR/no_effect_replace.rs:4:13 + | +LL | let _ = "12345".replace('1', "1"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::no-effect-replace` implied by `-D warnings` + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:5:13 + | +LL | let _ = "12345".replace("12", "12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:6:13 + | +LL | let _ = String::new().replace("12", "12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:8:13 + | +LL | let _ = "12345".replacen('1', "1", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:9:13 + | +LL | let _ = "12345".replacen("12", "12", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:10:13 + | +LL | let _ = String::new().replacen("12", "12", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:16:13 + | +LL | let _ = "hello".replace(&x.f(), &x.f()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:19:13 + | +LL | let _ = "hello".replace(&y(), &y()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/tests/ui/nonminimal_bool_methods.fixed b/tests/ui/nonminimal_bool_methods.fixed new file mode 100644 index 00000000000..aad44089de4 --- /dev/null +++ b/tests/ui/nonminimal_bool_methods.fixed @@ -0,0 +1,111 @@ +// run-rustfix +#![allow(unused, clippy::diverging_sub_expression)] +#![warn(clippy::nonminimal_bool)] + +fn methods_with_negation() { + let a: Option = unimplemented!(); + let b: Result = unimplemented!(); + let _ = a.is_some(); + let _ = a.is_none(); + let _ = a.is_none(); + let _ = a.is_some(); + let _ = b.is_err(); + let _ = b.is_ok(); + let _ = b.is_ok(); + let _ = b.is_err(); + let c = false; + let _ = a.is_none() || c; + let _ = a.is_none() && c; + let _ = !(!c ^ c) || a.is_none(); + let _ = (!c ^ c) || a.is_none(); + let _ = !c ^ c || a.is_none(); +} + +// Simplified versions of https://github.com/rust-lang/rust-clippy/issues/2638 +// clippy::nonminimal_bool should only check the built-in Result and Some type, not +// any other types like the following. +enum CustomResultOk { + Ok, + Err(E), +} +enum CustomResultErr { + Ok, + Err(E), +} +enum CustomSomeSome { + Some(T), + None, +} +enum CustomSomeNone { + Some(T), + None, +} + +impl CustomResultOk { + pub fn is_ok(&self) -> bool { + true + } +} + +impl CustomResultErr { + pub fn is_err(&self) -> bool { + true + } +} + +impl CustomSomeSome { + pub fn is_some(&self) -> bool { + true + } +} + +impl CustomSomeNone { + pub fn is_none(&self) -> bool { + true + } +} + +fn dont_warn_for_custom_methods_with_negation() { + let res = CustomResultOk::Err("Error"); + // Should not warn and suggest 'is_err()' because the type does not + // implement is_err(). + if !res.is_ok() {} + + let res = CustomResultErr::Err("Error"); + // Should not warn and suggest 'is_ok()' because the type does not + // implement is_ok(). + if !res.is_err() {} + + let res = CustomSomeSome::Some("thing"); + // Should not warn and suggest 'is_none()' because the type does not + // implement is_none(). + if !res.is_some() {} + + let res = CustomSomeNone::Some("thing"); + // Should not warn and suggest 'is_some()' because the type does not + // implement is_some(). + if !res.is_none() {} +} + +// Only Built-in Result and Some types should suggest the negated alternative +fn warn_for_built_in_methods_with_negation() { + let res: Result = Ok(1); + if res.is_err() {} + if res.is_ok() {} + + let res = Some(1); + if res.is_none() {} + if res.is_some() {} +} + +#[allow(clippy::neg_cmp_op_on_partial_ord)] +fn dont_warn_for_negated_partial_ord_comparison() { + let a: f64 = unimplemented!(); + let b: f64 = unimplemented!(); + let _ = !(a < b); + let _ = !(a <= b); + let _ = !(a > b); + let _ = !(a >= b); +} + +fn main() {} diff --git a/tests/ui/nonminimal_bool_methods.rs b/tests/ui/nonminimal_bool_methods.rs index d0a289b7ea4..b9074da8427 100644 --- a/tests/ui/nonminimal_bool_methods.rs +++ b/tests/ui/nonminimal_bool_methods.rs @@ -1,3 +1,4 @@ +// run-rustfix #![allow(unused, clippy::diverging_sub_expression)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/nonminimal_bool_methods.stderr b/tests/ui/nonminimal_bool_methods.stderr index a2df889d623..21b84db8589 100644 --- a/tests/ui/nonminimal_bool_methods.stderr +++ b/tests/ui/nonminimal_bool_methods.stderr @@ -1,5 +1,5 @@ error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:8:13 + --> $DIR/nonminimal_bool_methods.rs:9:13 | LL | let _ = !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` @@ -7,73 +7,73 @@ LL | let _ = !a.is_some(); = note: `-D clippy::nonminimal-bool` implied by `-D warnings` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:10:13 + --> $DIR/nonminimal_bool_methods.rs:11:13 | LL | let _ = !a.is_none(); | ^^^^^^^^^^^^ help: try: `a.is_some()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:12:13 + --> $DIR/nonminimal_bool_methods.rs:13:13 | LL | let _ = !b.is_err(); | ^^^^^^^^^^^ help: try: `b.is_ok()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:14:13 + --> $DIR/nonminimal_bool_methods.rs:15:13 | LL | let _ = !b.is_ok(); | ^^^^^^^^^^ help: try: `b.is_err()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:16:13 + --> $DIR/nonminimal_bool_methods.rs:17:13 | LL | let _ = !(a.is_some() && !c); | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() || c` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:17:13 + --> $DIR/nonminimal_bool_methods.rs:18:13 | LL | let _ = !(a.is_some() || !c); | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() && c` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:18:26 + --> $DIR/nonminimal_bool_methods.rs:19:26 | LL | let _ = !(!c ^ c) || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:19:25 + --> $DIR/nonminimal_bool_methods.rs:20:25 | LL | let _ = (!c ^ c) || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:20:23 + --> $DIR/nonminimal_bool_methods.rs:21:23 | LL | let _ = !c ^ c || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:92:8 + --> $DIR/nonminimal_bool_methods.rs:93:8 | LL | if !res.is_ok() {} | ^^^^^^^^^^^^ help: try: `res.is_err()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:93:8 + --> $DIR/nonminimal_bool_methods.rs:94:8 | LL | if !res.is_err() {} | ^^^^^^^^^^^^^ help: try: `res.is_ok()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:96:8 + --> $DIR/nonminimal_bool_methods.rs:97:8 | LL | if !res.is_some() {} | ^^^^^^^^^^^^^^ help: try: `res.is_none()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:97:8 + --> $DIR/nonminimal_bool_methods.rs:98:8 | LL | if !res.is_none() {} | ^^^^^^^^^^^^^^ help: try: `res.is_some()` diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index f4977199711..85d021b2f25 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -49,6 +49,13 @@ fn main() { x >= 10 && x <= -10; (-3. ..=3.).contains(&y); y >= 3. && y <= -3.; + + // Fix #8745 + let z = 42; + (0..=10).contains(&x) && (0..=10).contains(&z); + !(0..10).contains(&x) || !(0..10).contains(&z); + // Make sure operators in parens don't give a breaking suggestion + ((x % 2 == 0) || (x < 0)) || (x >= 10); } // Fix #6373 diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 9e2180b0c99..9a7a75dc132 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -49,6 +49,13 @@ fn main() { x >= 10 && x <= -10; y >= -3. && y <= 3.; y >= 3. && y <= -3.; + + // Fix #8745 + let z = 42; + (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); + (x < 0) || (x >= 10) || (z < 0) || (z >= 10); + // Make sure operators in parens don't give a breaking suggestion + ((x % 2 == 0) || (x < 0)) || (x >= 10); } // Fix #6373 diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 1817ee1715d..936859db5a1 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -96,5 +96,29 @@ error: manual `RangeInclusive::contains` implementation LL | y >= -3. && y <= 3.; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` -error: aborting due to 16 previous errors +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:55:30 + | +LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); + | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:55:5 + | +LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); + | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:56:29 + | +LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:56:5 + | +LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)` + +error: aborting due to 20 previous errors diff --git a/tests/ui/rc_buffer.fixed b/tests/ui/rc_buffer.fixed new file mode 100644 index 00000000000..8910c01b1fc --- /dev/null +++ b/tests/ui/rc_buffer.fixed @@ -0,0 +1,28 @@ +// run-rustfix +#![warn(clippy::rc_buffer)] +#![allow(dead_code, unused_imports)] + +use std::cell::RefCell; +use std::ffi::OsString; +use std::path::PathBuf; +use std::rc::Rc; + +struct S { + // triggers lint + bad1: Rc, + bad2: Rc, + bad3: Rc<[u8]>, + bad4: Rc, + // does not trigger lint + good1: Rc>, +} + +// triggers lint +fn func_bad1(_: Rc) {} +fn func_bad2(_: Rc) {} +fn func_bad3(_: Rc<[u8]>) {} +fn func_bad4(_: Rc) {} +// does not trigger lint +fn func_good1(_: Rc>) {} + +fn main() {} diff --git a/tests/ui/rc_buffer.rs b/tests/ui/rc_buffer.rs index 1fa98643936..1e63a43262e 100644 --- a/tests/ui/rc_buffer.rs +++ b/tests/ui/rc_buffer.rs @@ -1,4 +1,6 @@ +// run-rustfix #![warn(clippy::rc_buffer)] +#![allow(dead_code, unused_imports)] use std::cell::RefCell; use std::ffi::OsString; diff --git a/tests/ui/rc_buffer.stderr b/tests/ui/rc_buffer.stderr index e4cc169af07..9ed028e3df4 100644 --- a/tests/ui/rc_buffer.stderr +++ b/tests/ui/rc_buffer.stderr @@ -1,5 +1,5 @@ error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:10:11 + --> $DIR/rc_buffer.rs:12:11 | LL | bad1: Rc, | ^^^^^^^^^^ help: try: `Rc` @@ -7,43 +7,43 @@ LL | bad1: Rc, = note: `-D clippy::rc-buffer` implied by `-D warnings` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:11:11 + --> $DIR/rc_buffer.rs:13:11 | LL | bad2: Rc, | ^^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:12:11 + --> $DIR/rc_buffer.rs:14:11 | LL | bad3: Rc>, | ^^^^^^^^^^^ help: try: `Rc<[u8]>` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:13:11 + --> $DIR/rc_buffer.rs:15:11 | LL | bad4: Rc, | ^^^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:19:17 + --> $DIR/rc_buffer.rs:21:17 | LL | fn func_bad1(_: Rc) {} | ^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:20:17 + --> $DIR/rc_buffer.rs:22:17 | LL | fn func_bad2(_: Rc) {} | ^^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:21:17 + --> $DIR/rc_buffer.rs:23:17 | LL | fn func_bad3(_: Rc>) {} | ^^^^^^^^^^^ help: try: `Rc<[u8]>` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:22:17 + --> $DIR/rc_buffer.rs:24:17 | LL | fn func_bad4(_: Rc) {} | ^^^^^^^^^^^^ help: try: `Rc` diff --git a/tests/ui/rc_buffer_arc.fixed b/tests/ui/rc_buffer_arc.fixed new file mode 100644 index 00000000000..13dd6f5fcd1 --- /dev/null +++ b/tests/ui/rc_buffer_arc.fixed @@ -0,0 +1,27 @@ +// run-rustfix +#![warn(clippy::rc_buffer)] +#![allow(dead_code, unused_imports)] + +use std::ffi::OsString; +use std::path::PathBuf; +use std::sync::{Arc, Mutex}; + +struct S { + // triggers lint + bad1: Arc, + bad2: Arc, + bad3: Arc<[u8]>, + bad4: Arc, + // does not trigger lint + good1: Arc>, +} + +// triggers lint +fn func_bad1(_: Arc) {} +fn func_bad2(_: Arc) {} +fn func_bad3(_: Arc<[u8]>) {} +fn func_bad4(_: Arc) {} +// does not trigger lint +fn func_good1(_: Arc>) {} + +fn main() {} diff --git a/tests/ui/rc_buffer_arc.rs b/tests/ui/rc_buffer_arc.rs index 5d586584817..1a521bfeb7c 100644 --- a/tests/ui/rc_buffer_arc.rs +++ b/tests/ui/rc_buffer_arc.rs @@ -1,4 +1,6 @@ +// run-rustfix #![warn(clippy::rc_buffer)] +#![allow(dead_code, unused_imports)] use std::ffi::OsString; use std::path::PathBuf; diff --git a/tests/ui/rc_buffer_arc.stderr b/tests/ui/rc_buffer_arc.stderr index 8252270d2ac..911feea7352 100644 --- a/tests/ui/rc_buffer_arc.stderr +++ b/tests/ui/rc_buffer_arc.stderr @@ -1,5 +1,5 @@ error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:9:11 + --> $DIR/rc_buffer_arc.rs:11:11 | LL | bad1: Arc, | ^^^^^^^^^^^ help: try: `Arc` @@ -7,43 +7,43 @@ LL | bad1: Arc, = note: `-D clippy::rc-buffer` implied by `-D warnings` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:10:11 + --> $DIR/rc_buffer_arc.rs:12:11 | LL | bad2: Arc, | ^^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:11:11 + --> $DIR/rc_buffer_arc.rs:13:11 | LL | bad3: Arc>, | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:12:11 + --> $DIR/rc_buffer_arc.rs:14:11 | LL | bad4: Arc, | ^^^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:18:17 + --> $DIR/rc_buffer_arc.rs:20:17 | LL | fn func_bad1(_: Arc) {} | ^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:19:17 + --> $DIR/rc_buffer_arc.rs:21:17 | LL | fn func_bad2(_: Arc) {} | ^^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:20:17 + --> $DIR/rc_buffer_arc.rs:22:17 | LL | fn func_bad3(_: Arc>) {} | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:21:17 + --> $DIR/rc_buffer_arc.rs:23:17 | LL | fn func_bad4(_: Arc) {} | ^^^^^^^^^^^^^ help: try: `Arc` diff --git a/tests/ui/rc_clone_in_vec_init/arc.stderr b/tests/ui/rc_clone_in_vec_init/arc.stderr index ce84186c8e3..cd7d91e1206 100644 --- a/tests/ui/rc_clone_in_vec_init/arc.stderr +++ b/tests/ui/rc_clone_in_vec_init/arc.stderr @@ -1,4 +1,4 @@ -error: calling `Arc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/arc.rs:7:13 | LL | let v = vec![Arc::new("x".to_string()); 2]; @@ -10,19 +10,19 @@ help: consider initializing each `Arc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string()))); +LL + (0..2).for_each(|_| v.push(Arc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Arc` initialization to a variable | LL ~ let v = { -LL + let data = Arc::new("x".to_string()); +LL + let data = Arc::new(..); LL + vec![data; 2] LL ~ }; | -error: calling `Arc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/arc.rs:15:21 | LL | let v = vec![Arc::new("x".to_string()); 2]; @@ -33,19 +33,19 @@ help: consider initializing each `Arc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string()))); +LL + (0..2).for_each(|_| v.push(Arc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Arc` initialization to a variable | LL ~ let v = { -LL + let data = Arc::new("x".to_string()); +LL + let data = Arc::new(..); LL + vec![data; 2] LL ~ }; | -error: calling `Arc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/arc.rs:21:13 | LL | let v = vec![ @@ -75,7 +75,7 @@ LL + vec![data; 2] LL ~ }; | -error: calling `Arc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/arc.rs:30:14 | LL | let v1 = vec![ diff --git a/tests/ui/rc_clone_in_vec_init/rc.stderr b/tests/ui/rc_clone_in_vec_init/rc.stderr index 0f5cc0cf98f..fe861afe054 100644 --- a/tests/ui/rc_clone_in_vec_init/rc.stderr +++ b/tests/ui/rc_clone_in_vec_init/rc.stderr @@ -1,4 +1,4 @@ -error: calling `Rc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/rc.rs:8:13 | LL | let v = vec![Rc::new("x".to_string()); 2]; @@ -10,19 +10,19 @@ help: consider initializing each `Rc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string()))); +LL + (0..2).for_each(|_| v.push(Rc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Rc` initialization to a variable | LL ~ let v = { -LL + let data = Rc::new("x".to_string()); +LL + let data = Rc::new(..); LL + vec![data; 2] LL ~ }; | -error: calling `Rc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/rc.rs:16:21 | LL | let v = vec![Rc::new("x".to_string()); 2]; @@ -33,19 +33,19 @@ help: consider initializing each `Rc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string()))); +LL + (0..2).for_each(|_| v.push(Rc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Rc` initialization to a variable | LL ~ let v = { -LL + let data = Rc::new("x".to_string()); +LL + let data = Rc::new(..); LL + vec![data; 2] LL ~ }; | -error: calling `Rc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/rc.rs:22:13 | LL | let v = vec![ @@ -75,7 +75,7 @@ LL + vec![data; 2] LL ~ }; | -error: calling `Rc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/rc.rs:31:14 | LL | let v1 = vec![ diff --git a/tests/ui/rc_clone_in_vec_init/weak.rs b/tests/ui/rc_clone_in_vec_init/weak.rs new file mode 100644 index 00000000000..693c9b553c5 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/weak.rs @@ -0,0 +1,83 @@ +#![warn(clippy::rc_clone_in_vec_init)] +use std::rc::{Rc, Weak as UnSyncWeak}; +use std::sync::{Arc, Mutex, Weak as SyncWeak}; + +fn main() {} + +fn should_warn_simple_case() { + let v = vec![SyncWeak::::new(); 2]; + let v2 = vec![UnSyncWeak::::new(); 2]; + + let v = vec![Rc::downgrade(&Rc::new("x".to_string())); 2]; + let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2]; +} + +fn should_warn_simple_case_with_big_indentation() { + if true { + let k = 1; + dbg!(k); + if true { + let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2]; + let v2 = vec![Rc::downgrade(&Rc::new("x".to_string())); 2]; + } + } +} + +fn should_warn_complex_case() { + let v = vec![ + Arc::downgrade(&Arc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + }))); + 2 + ]; + + let v1 = vec![ + Rc::downgrade(&Rc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + }))); + 2 + ]; +} + +fn should_not_warn_custom_weak() { + #[derive(Clone)] + struct Weak; + + impl Weak { + fn new() -> Self { + Weak + } + } + + let v = vec![Weak::new(); 2]; +} + +fn should_not_warn_vec_from_elem_but_not_weak() { + let v = vec![String::new(); 2]; + let v1 = vec![1; 2]; + let v2 = vec![ + Box::new(Arc::downgrade(&Arc::new({ + let y = 3; + dbg!(y); + y + }))); + 2 + ]; + let v3 = vec![ + Box::new(Rc::downgrade(&Rc::new({ + let y = 3; + dbg!(y); + y + }))); + 2 + ]; +} + +fn should_not_warn_vec_macro_but_not_from_elem() { + let v = vec![Arc::downgrade(&Arc::new("x".to_string()))]; + let v = vec![Rc::downgrade(&Rc::new("x".to_string()))]; +} diff --git a/tests/ui/rc_clone_in_vec_init/weak.stderr b/tests/ui/rc_clone_in_vec_init/weak.stderr new file mode 100644 index 00000000000..4a21946ccdf --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/weak.stderr @@ -0,0 +1,201 @@ +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:8:13 + | +LL | let v = vec![SyncWeak::::new(); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings` + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(SyncWeak::::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = SyncWeak::::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:9:14 + | +LL | let v2 = vec![UnSyncWeak::::new(); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v2 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(UnSyncWeak::::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v2 = { +LL + let data = UnSyncWeak::::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:11:13 + | +LL | let v = vec![Rc::downgrade(&Rc::new("x".to_string())); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = Rc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:12:13 + | +LL | let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:20:21 + | +LL | let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:21:22 + | +LL | let v2 = vec![Rc::downgrade(&Rc::new("x".to_string())); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v2 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v2 = { +LL + let data = Rc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:27:13 + | +LL | let v = vec![ + | _____________^ +LL | | Arc::downgrade(&Arc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:36:14 + | +LL | let v1 = vec![ + | ______________^ +LL | | Rc::downgrade(&Rc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v1 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v1 = { +LL + let data = Rc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: aborting due to 8 previous errors + diff --git a/tests/ui/recursive_format_impl.rs b/tests/ui/recursive_format_impl.rs index f72fc77ab99..cb6ba36b14c 100644 --- a/tests/ui/recursive_format_impl.rs +++ b/tests/ui/recursive_format_impl.rs @@ -2,7 +2,8 @@ #![allow( clippy::inherent_to_string_shadow_display, clippy::to_string_in_format_args, - clippy::deref_addrof + clippy::deref_addrof, + clippy::borrow_deref_ref )] use std::fmt; diff --git a/tests/ui/recursive_format_impl.stderr b/tests/ui/recursive_format_impl.stderr index 1a717ac92d8..84ce69df566 100644 --- a/tests/ui/recursive_format_impl.stderr +++ b/tests/ui/recursive_format_impl.stderr @@ -1,5 +1,5 @@ error: using `self.to_string` in `fmt::Display` implementation will cause infinite recursion - --> $DIR/recursive_format_impl.rs:29:25 + --> $DIR/recursive_format_impl.rs:30:25 | LL | write!(f, "{}", self.to_string()) | ^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | write!(f, "{}", self.to_string()) = note: `-D clippy::recursive-format-impl` implied by `-D warnings` error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:73:9 + --> $DIR/recursive_format_impl.rs:74:9 | LL | write!(f, "{}", self) | ^^^^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | write!(f, "{}", self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:82:9 + --> $DIR/recursive_format_impl.rs:83:9 | LL | write!(f, "{}", &self) | ^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | write!(f, "{}", &self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:88:9 + --> $DIR/recursive_format_impl.rs:89:9 | LL | write!(f, "{:?}", &self) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | write!(f, "{:?}", &self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:97:9 + --> $DIR/recursive_format_impl.rs:98:9 | LL | write!(f, "{}", &&&self) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | write!(f, "{}", &&&self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:171:9 + --> $DIR/recursive_format_impl.rs:172:9 | LL | write!(f, "{}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | write!(f, "{}", &*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:177:9 + --> $DIR/recursive_format_impl.rs:178:9 | LL | write!(f, "{:?}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | write!(f, "{:?}", &*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:193:9 + --> $DIR/recursive_format_impl.rs:194:9 | LL | write!(f, "{}", *self) | ^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | write!(f, "{}", *self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:209:9 + --> $DIR/recursive_format_impl.rs:210:9 | LL | write!(f, "{}", **&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,7 +71,7 @@ LL | write!(f, "{}", **&&*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:225:9 + --> $DIR/recursive_format_impl.rs:226:9 | LL | write!(f, "{}", &&**&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index a394ef8f25c..1fa9fc749a9 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -88,4 +88,11 @@ pub async fn foo2(_a: i32, _b: i64) { let _b = _a; } +fn ice_8748() { + let _ = [0; { + let x = 1; + if let Some(x) = Some(1) { x } else { 1 } + }]; +} + fn main() {} diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index f83a6dd0eb2..4347610f393 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -7,8 +7,10 @@ #![allow(unused_assignments)] #![allow(dead_code)] +use std::num::ParseIntError; use std::ops::Deref; use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::RwLock; use std::sync::{Mutex, MutexGuard}; struct State {} @@ -552,4 +554,41 @@ fn should_not_cause_stack_overflow() { } } +fn should_not_produce_lint_for_try_desugar() -> Result { + // TryDesugar (i.e. using `?` for a Result type) will turn into a match but is out of scope + // for this lint + let rwlock = RwLock::new("1".to_string()); + let result = rwlock.read().unwrap().parse::()?; + println!("{}", result); + rwlock.write().unwrap().push('2'); + Ok(result) +} + +struct ResultReturner { + s: String, +} + +impl ResultReturner { + fn to_number(&self) -> Result { + self.s.parse::() + } +} + +fn should_trigger_lint_for_non_ref_move_and_clone_suggestion() { + let rwlock = RwLock::::new(ResultReturner { s: "1".to_string() }); + match rwlock.read().unwrap().to_number() { + Ok(n) => println!("Converted to number: {}", n), + Err(e) => println!("Could not convert {} to number", e), + }; +} + +fn should_trigger_lint_for_read_write_lock_for_loop() { + // For-in loops desugar to match expressions and are prone to the type of deadlock this lint is + // designed to look for. + let rwlock = RwLock::>::new(vec!["1".to_string()]); + for s in rwlock.read().unwrap().iter() { + println!("{}", s); + } +} + fn main() {} diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index af160564985..303f3c1df03 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -1,5 +1,5 @@ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:57:11 + --> $DIR/significant_drop_in_scrutinee.rs:59:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:130:11 + --> $DIR/significant_drop_in_scrutinee.rs:132:11 | LL | match s.lock_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:151:11 + --> $DIR/significant_drop_in_scrutinee.rs:153:11 | LL | match s.lock_m_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:199:11 + --> $DIR/significant_drop_in_scrutinee.rs:201:11 | LL | match counter.temp_increment().len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:222:16 + --> $DIR/significant_drop_in_scrutinee.rs:224:16 | LL | match (mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL ~ match (value, true) { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:231:22 + --> $DIR/significant_drop_in_scrutinee.rs:233:22 | LL | match (true, mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL ~ match (true, value, true) { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:241:16 + --> $DIR/significant_drop_in_scrutinee.rs:243:16 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL ~ match (value, true, mutex2.lock().unwrap().s.len()) { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:241:54 + --> $DIR/significant_drop_in_scrutinee.rs:243:54 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,19 +96,19 @@ LL ~ match (mutex1.lock().unwrap().s.len(), true, value) { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:252:15 + --> $DIR/significant_drop_in_scrutinee.rs:254:15 | LL | match mutex3.lock().unwrap().s.as_str() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:262:22 + --> $DIR/significant_drop_in_scrutinee.rs:264:22 | LL | match (true, mutex3.lock().unwrap().s.as_str()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:281:11 + --> $DIR/significant_drop_in_scrutinee.rs:283:11 | LL | match mutex.lock().unwrap().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:288:11 + --> $DIR/significant_drop_in_scrutinee.rs:290:11 | LL | match 1 < mutex.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:306:11 + --> $DIR/significant_drop_in_scrutinee.rs:308:11 | LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:317:11 + --> $DIR/significant_drop_in_scrutinee.rs:319:11 | LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:352:11 + --> $DIR/significant_drop_in_scrutinee.rs:354:11 | LL | match get_mutex_guard().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -168,7 +168,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:369:11 + --> $DIR/significant_drop_in_scrutinee.rs:371:11 | LL | match match i { | ___________^ @@ -191,7 +191,7 @@ LL + .len() ... error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:395:11 + --> $DIR/significant_drop_in_scrutinee.rs:397:11 | LL | match if i > 1 { | ___________^ @@ -214,7 +214,7 @@ LL + .s ... error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:449:11 + --> $DIR/significant_drop_in_scrutinee.rs:451:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -226,13 +226,13 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:477:11 + --> $DIR/significant_drop_in_scrutinee.rs:479:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:496:11 + --> $DIR/significant_drop_in_scrutinee.rs:498:11 | LL | match mutex.lock().unwrap().i = i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -244,7 +244,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:502:11 + --> $DIR/significant_drop_in_scrutinee.rs:504:11 | LL | match i = mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -256,7 +256,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:508:11 + --> $DIR/significant_drop_in_scrutinee.rs:510:11 | LL | match mutex.lock().unwrap().i += 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -268,7 +268,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:514:11 + --> $DIR/significant_drop_in_scrutinee.rs:516:11 | LL | match i += mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,5 +279,17 @@ LL ~ i += mutex.lock().unwrap().i; LL ~ match () { | -error: aborting due to 23 previous errors +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:579:11 + | +LL | match rwlock.read().unwrap().to_number() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: temporary with significant drop in for loop + --> $DIR/significant_drop_in_scrutinee.rs:589:14 + | +LL | for s in rwlock.read().unwrap().iter() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors diff --git a/tests/ui/suspicious_operation_groupings.fixed b/tests/ui/suspicious_operation_groupings.fixed new file mode 100644 index 00000000000..ede8a39fed7 --- /dev/null +++ b/tests/ui/suspicious_operation_groupings.fixed @@ -0,0 +1,209 @@ +// run-rustfix +#![warn(clippy::suspicious_operation_groupings)] +#![allow(dead_code, unused_parens, clippy::eq_op)] + +struct Vec3 { + x: f64, + y: f64, + z: f64, +} + +impl Eq for Vec3 {} + +impl PartialEq for Vec3 { + fn eq(&self, other: &Self) -> bool { + // This should trigger the lint because `self.x` is compared to `other.y` + self.x == other.x && self.y == other.y && self.z == other.z + } +} + +struct S { + a: i32, + b: i32, + c: i32, + d: i32, +} + +fn buggy_ab_cmp(s1: &S, s2: &S) -> bool { + // There's no `s1.b` + s1.a < s2.a && s1.b < s2.b +} + +struct SaOnly { + a: i32, +} + +impl S { + fn a(&self) -> i32 { + 0 + } +} + +fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SaOnly) -> bool { + // This is superficially similar to `buggy_ab_cmp`, but we should not suggest + // `s2.b` since that is invalid. + s1.a < s2.a && s1.a() < s1.b +} + +fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SaOnly) -> bool { + macro_rules! s1 { + () => { + S { + a: 1, + b: 1, + c: 1, + d: 1, + } + }; + } + + // This is superficially similar to `buggy_ab_cmp`, but we should not suggest + // `s2.b` since that is invalid. + s1.a < s2.a && s1!().a < s1.b +} + +fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SaOnly) -> bool { + // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid + s1.a < s2.a && s1.b < s1.b +} + +fn permissable(s1: &S, s2: &S) -> bool { + // Something like this seems like it might actually be what is desired. + s1.a == s2.b +} + +fn non_boolean_operators(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c + s1.d * s2.d +} + +fn odd_number_of_pairs(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_middle_change_left(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_middle_change_right(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_start(s1: &S, s2: &S) -> i32 { + // There's no `s2.a` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_end(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn the_cross_product_should_not_lint(s1: &S, s2: &S) -> (i32, i32, i32) { + ( + s1.b * s2.c - s1.c * s2.b, + s1.c * s2.a - s1.a * s2.c, + s1.a * s2.b - s1.b * s2.a, + ) +} + +fn outer_parens_simple(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + (s1.a * s2.a + s1.b * s2.b) +} + +fn outer_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (s1.a * s2.a + s1.b * s2.b + s1.c * s2.c + s1.d * s2.d) +} + +fn inner_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d) +} + +fn outer_and_some_inner_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d)) +} + +fn all_parens_balanced_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.c) + (s1.d * s2.d))) +} + +fn all_parens_left_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c)) + (s1.d * s2.d)) +} + +fn all_parens_right_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d))) +} + +fn inside_other_binop_expression(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + (s1.a * s2.a + s1.b * s2.b) / 2 +} + +fn inside_function_call(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + i32::swap_bytes(s1.a * s2.a + s1.b * s2.b) +} + +fn inside_larger_boolean_expression(s1: &S, s2: &S) -> bool { + // There's no `s1.c` + s1.a > 0 && s1.b > 0 && s1.c == s2.c && s1.d == s2.d +} + +fn inside_larger_boolean_expression_with_unsorted_ops(s1: &S, s2: &S) -> bool { + // There's no `s1.c` + s1.a > 0 && s1.c == s2.c && s1.b > 0 && s1.d == s2.d +} + +struct Nested { + inner: ((i32,), (i32,), (i32,)), +} + +fn changed_middle_ident(n1: &Nested, n2: &Nested) -> bool { + // There's no `n2.inner.2.0` + (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 +} + +// `eq_op` should catch this one. +fn changed_initial_ident(n1: &Nested, n2: &Nested) -> bool { + // There's no `n2.inner.0.0` + (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 +} + +fn inside_fn_with_similar_expression(s1: &S, s2: &S, strict: bool) -> bool { + if strict { + s1.a < s2.a && s1.b < s2.b + } else { + // There's no `s1.b` in this subexpression + s1.a <= s2.a && s1.b <= s2.b + } +} + +fn inside_an_if_statement(s1: &mut S, s2: &S) { + // There's no `s1.b` + if s1.a < s2.a && s1.b < s2.b { + s1.c = s2.c; + } +} + +fn maximum_unary_minus_right_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.c) + -(-s1.d * -s2.d))) +} + +fn unary_minus_and_an_if_expression(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + -(if -s1.a < -s2.a && -s1.b < -s2.b { s1.c } else { s2.a }) +} + +fn main() {} diff --git a/tests/ui/suspicious_operation_groupings.rs b/tests/ui/suspicious_operation_groupings.rs index 3201d5de0f3..26ce97bb37f 100644 --- a/tests/ui/suspicious_operation_groupings.rs +++ b/tests/ui/suspicious_operation_groupings.rs @@ -1,5 +1,6 @@ +// run-rustfix #![warn(clippy::suspicious_operation_groupings)] -#![allow(clippy::eq_op)] +#![allow(dead_code, unused_parens, clippy::eq_op)] struct Vec3 { x: f64, diff --git a/tests/ui/suspicious_operation_groupings.stderr b/tests/ui/suspicious_operation_groupings.stderr index baf9bc74b00..29f229245fe 100644 --- a/tests/ui/suspicious_operation_groupings.stderr +++ b/tests/ui/suspicious_operation_groupings.stderr @@ -1,5 +1,5 @@ error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:15:9 + --> $DIR/suspicious_operation_groupings.rs:16:9 | LL | self.x == other.y && self.y == other.y && self.z == other.z | ^^^^^^^^^^^^^^^^^ help: did you mean: `self.x == other.x` @@ -7,151 +7,151 @@ LL | self.x == other.y && self.y == other.y && self.z == other.z = note: `-D clippy::suspicious-operation-groupings` implied by `-D warnings` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:28:20 + --> $DIR/suspicious_operation_groupings.rs:29:20 | LL | s1.a < s2.a && s1.a < s2.b | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:76:33 + --> $DIR/suspicious_operation_groupings.rs:77:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:81:19 + --> $DIR/suspicious_operation_groupings.rs:82:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:81:19 + --> $DIR/suspicious_operation_groupings.rs:82:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:86:19 + --> $DIR/suspicious_operation_groupings.rs:87:19 | LL | s1.a * s2.a + s2.b * s2.b + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:91:19 + --> $DIR/suspicious_operation_groupings.rs:92:19 | LL | s1.a * s2.a + s1.b * s1.b + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:96:5 + --> $DIR/suspicious_operation_groupings.rs:97:5 | LL | s1.a * s1.a + s1.b * s2.b + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.a * s2.a` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:101:33 + --> $DIR/suspicious_operation_groupings.rs:102:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s1.c | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:114:20 + --> $DIR/suspicious_operation_groupings.rs:115:20 | LL | (s1.a * s2.a + s1.b * s1.b) | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:119:34 + --> $DIR/suspicious_operation_groupings.rs:120:34 | LL | (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:124:38 + --> $DIR/suspicious_operation_groupings.rs:125:38 | LL | (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:129:39 + --> $DIR/suspicious_operation_groupings.rs:130:39 | LL | ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:134:42 + --> $DIR/suspicious_operation_groupings.rs:135:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:134:42 + --> $DIR/suspicious_operation_groupings.rs:135:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:139:40 + --> $DIR/suspicious_operation_groupings.rs:140:40 | LL | (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:144:40 + --> $DIR/suspicious_operation_groupings.rs:145:40 | LL | ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:149:20 + --> $DIR/suspicious_operation_groupings.rs:150:20 | LL | (s1.a * s2.a + s2.b * s2.b) / 2 | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:154:35 + --> $DIR/suspicious_operation_groupings.rs:155:35 | LL | i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:159:29 + --> $DIR/suspicious_operation_groupings.rs:160:29 | LL | s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:164:17 + --> $DIR/suspicious_operation_groupings.rs:165:17 | LL | s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:173:77 + --> $DIR/suspicious_operation_groupings.rs:174:77 | LL | (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `(n1.inner.2).0 == (n2.inner.2).0` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:187:25 + --> $DIR/suspicious_operation_groupings.rs:188:25 | LL | s1.a <= s2.a && s1.a <= s2.b | ^^^^^^^^^^^^ help: did you mean: `s1.b <= s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:193:23 + --> $DIR/suspicious_operation_groupings.rs:194:23 | LL | if s1.a < s2.a && s1.a < s2.b { | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:200:48 + --> $DIR/suspicious_operation_groupings.rs:201:48 | LL | -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) | ^^^^^^^^^^^^^ help: did you mean: `-s1.c * -s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:205:27 + --> $DIR/suspicious_operation_groupings.rs:206:27 | LL | -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) | ^^^^^^^^^^^^^ help: did you mean: `-s1.b < -s2.b` diff --git a/tests/ui/swap_ptr_to_ref.fixed b/tests/ui/swap_ptr_to_ref.fixed new file mode 100644 index 00000000000..596b6ee919b --- /dev/null +++ b/tests/ui/swap_ptr_to_ref.fixed @@ -0,0 +1,24 @@ +// run-rustfix + +#![warn(clippy::swap_ptr_to_ref)] + +use core::ptr::addr_of_mut; + +fn main() { + let mut x = 0u32; + let y: *mut _ = &mut x; + let z: *mut _ = &mut x; + + unsafe { + core::ptr::swap(y, z); + core::ptr::swap(y, &mut x); + core::ptr::swap(&mut x, y); + core::ptr::swap(addr_of_mut!(x), addr_of_mut!(x)); + } + + let y = &mut x; + let mut z = 0u32; + let z = &mut z; + + core::mem::swap(y, z); +} diff --git a/tests/ui/swap_ptr_to_ref.rs b/tests/ui/swap_ptr_to_ref.rs new file mode 100644 index 00000000000..282f571211d --- /dev/null +++ b/tests/ui/swap_ptr_to_ref.rs @@ -0,0 +1,24 @@ +// run-rustfix + +#![warn(clippy::swap_ptr_to_ref)] + +use core::ptr::addr_of_mut; + +fn main() { + let mut x = 0u32; + let y: *mut _ = &mut x; + let z: *mut _ = &mut x; + + unsafe { + core::mem::swap(&mut *y, &mut *z); + core::mem::swap(&mut *y, &mut x); + core::mem::swap(&mut x, &mut *y); + core::mem::swap(&mut *addr_of_mut!(x), &mut *addr_of_mut!(x)); + } + + let y = &mut x; + let mut z = 0u32; + let z = &mut z; + + core::mem::swap(y, z); +} diff --git a/tests/ui/swap_ptr_to_ref.stderr b/tests/ui/swap_ptr_to_ref.stderr new file mode 100644 index 00000000000..401ce070869 --- /dev/null +++ b/tests/ui/swap_ptr_to_ref.stderr @@ -0,0 +1,28 @@ +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref.rs:13:9 + | +LL | core::mem::swap(&mut *y, &mut *z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(y, z)` + | + = note: `-D clippy::swap-ptr-to-ref` implied by `-D warnings` + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref.rs:14:9 + | +LL | core::mem::swap(&mut *y, &mut x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(y, &mut x)` + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref.rs:15:9 + | +LL | core::mem::swap(&mut x, &mut *y); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(&mut x, y)` + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref.rs:16:9 + | +LL | core::mem::swap(&mut *addr_of_mut!(x), &mut *addr_of_mut!(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(addr_of_mut!(x), addr_of_mut!(x))` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/swap_ptr_to_ref_unfixable.rs b/tests/ui/swap_ptr_to_ref_unfixable.rs new file mode 100644 index 00000000000..66ea7c6529b --- /dev/null +++ b/tests/ui/swap_ptr_to_ref_unfixable.rs @@ -0,0 +1,18 @@ +#![warn(clippy::swap_ptr_to_ref)] + +macro_rules! addr_of_mut_to_ref { + ($e:expr) => { + &mut *core::ptr::addr_of_mut!($e) + }; +} + +fn main() { + let mut x = 0u32; + let y: *mut _ = &mut x; + + unsafe { + core::mem::swap(addr_of_mut_to_ref!(x), &mut *y); + core::mem::swap(&mut *y, addr_of_mut_to_ref!(x)); + core::mem::swap(addr_of_mut_to_ref!(x), addr_of_mut_to_ref!(x)); + } +} diff --git a/tests/ui/swap_ptr_to_ref_unfixable.stderr b/tests/ui/swap_ptr_to_ref_unfixable.stderr new file mode 100644 index 00000000000..c261205d556 --- /dev/null +++ b/tests/ui/swap_ptr_to_ref_unfixable.stderr @@ -0,0 +1,22 @@ +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref_unfixable.rs:14:9 + | +LL | core::mem::swap(addr_of_mut_to_ref!(x), &mut *y); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::swap-ptr-to-ref` implied by `-D warnings` + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref_unfixable.rs:15:9 + | +LL | core::mem::swap(&mut *y, addr_of_mut_to_ref!(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref_unfixable.rs:16:9 + | +LL | core::mem::swap(addr_of_mut_to_ref!(x), addr_of_mut_to_ref!(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 5b688ce4d64..001c910239a 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -16,7 +16,8 @@ fn my_vec() -> MyVec { #[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)] #[warn(clippy::useless_transmute)] unsafe fn _generic<'a, T, U: 'a>(t: &'a T) { - let _: &'a T = core::intrinsics::transmute(t); + // FIXME: should lint + // let _: &'a T = core::intrinsics::transmute(t); let _: &'a U = core::intrinsics::transmute(t); @@ -48,6 +49,22 @@ fn useless() { let _ = (1 + 1_usize) as *const usize; } + + unsafe fn _f<'a, 'b>(x: &'a u32) -> &'b u32 { + std::mem::transmute(x) + } + + unsafe fn _f2<'a, 'b>(x: *const (dyn Iterator + 'a)) -> *const (dyn Iterator + 'b) { + std::mem::transmute(x) + } + + unsafe fn _f3<'a, 'b>(x: fn(&'a u32)) -> fn(&'b u32) { + std::mem::transmute(x) + } + + unsafe fn _f4<'a, 'b>(x: std::borrow::Cow<'a, str>) -> std::borrow::Cow<'b, str> { + std::mem::transmute(x) + } } struct Usize(usize); diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 72a386e8fa6..008b4a981d7 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -1,73 +1,67 @@ -error: transmute from a type (`&T`) to itself - --> $DIR/transmute.rs:19:20 +error: transmute from a reference to a pointer + --> $DIR/transmute.rs:24:23 | -LL | let _: &'a T = core::intrinsics::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: *const T = core::intrinsics::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` | = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:23:23 - | -LL | let _: *const T = core::intrinsics::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` - -error: transmute from a reference to a pointer - --> $DIR/transmute.rs:25:21 + --> $DIR/transmute.rs:26:21 | LL | let _: *mut T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:27:23 + --> $DIR/transmute.rs:28:23 | LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:33:27 + --> $DIR/transmute.rs:34:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:35:27 + --> $DIR/transmute.rs:36:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:37:27 + --> $DIR/transmute.rs:38:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:39:27 + --> $DIR/transmute.rs:40:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:41:27 + --> $DIR/transmute.rs:42:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> $DIR/transmute.rs:43:31 + --> $DIR/transmute.rs:44:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> $DIR/transmute.rs:47:31 + --> $DIR/transmute.rs:48:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:62:24 + --> $DIR/transmute.rs:79:24 | LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,25 +69,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:64:24 + --> $DIR/transmute.rs:81:24 | LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> $DIR/transmute.rs:66:31 + --> $DIR/transmute.rs:83:31 | LL | let _: *const Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> $DIR/transmute.rs:68:29 + --> $DIR/transmute.rs:85:29 | LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u32` to a `char` - --> $DIR/transmute.rs:74:28 + --> $DIR/transmute.rs:91:28 | LL | let _: char = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` @@ -101,13 +95,13 @@ LL | let _: char = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` error: transmute from a `i32` to a `char` - --> $DIR/transmute.rs:75:28 + --> $DIR/transmute.rs:92:28 | LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:84:28 + --> $DIR/transmute.rs:101:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -115,7 +109,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:90:31 + --> $DIR/transmute.rs:107:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` @@ -123,25 +117,25 @@ LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:91:31 + --> $DIR/transmute.rs:108:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `u64` to a `f64` - --> $DIR/transmute.rs:92:31 + --> $DIR/transmute.rs:109:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` error: transmute from a `i64` to a `f64` - --> $DIR/transmute.rs:93:31 + --> $DIR/transmute.rs:110:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:113:30 + --> $DIR/transmute.rs:130:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -149,85 +143,85 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:114:30 + --> $DIR/transmute.rs:131:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:115:31 + --> $DIR/transmute.rs:132:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:116:30 + --> $DIR/transmute.rs:133:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:117:30 + --> $DIR/transmute.rs:134:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:118:31 + --> $DIR/transmute.rs:135:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> $DIR/transmute.rs:119:30 + --> $DIR/transmute.rs:136:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> $DIR/transmute.rs:120:30 + --> $DIR/transmute.rs:137:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:125:30 + --> $DIR/transmute.rs:142:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:126:30 + --> $DIR/transmute.rs:143:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:127:31 + --> $DIR/transmute.rs:144:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:128:30 + --> $DIR/transmute.rs:145:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:129:30 + --> $DIR/transmute.rs:146:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:130:31 + --> $DIR/transmute.rs:147:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:140:28 + --> $DIR/transmute.rs:157:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -235,16 +229,16 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:141:32 + --> $DIR/transmute.rs:158:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:142:30 + --> $DIR/transmute.rs:159:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 39 previous errors +error: aborting due to 38 previous errors diff --git a/tests/ui/transmute_undefined_repr.rs b/tests/ui/transmute_undefined_repr.rs index b06ed4a9173..ebcaa7a84cf 100644 --- a/tests/ui/transmute_undefined_repr.rs +++ b/tests/ui/transmute_undefined_repr.rs @@ -1,5 +1,5 @@ #![warn(clippy::transmute_undefined_repr)] -#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)] +#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref, clippy::useless_transmute)] use core::any::TypeId; use core::ffi::c_void; diff --git a/tests/ui/unicode.fixed b/tests/ui/unicode.fixed new file mode 100644 index 00000000000..328cda369e1 --- /dev/null +++ b/tests/ui/unicode.fixed @@ -0,0 +1,36 @@ +// run-rustfix +#[warn(clippy::invisible_characters)] +fn zero() { + print!("Here >\u{200B}< is a ZWS, and \u{200B}another"); + print!("This\u{200B}is\u{200B}fine"); + print!("Here >\u{AD}< is a SHY, and \u{AD}another"); + print!("This\u{ad}is\u{ad}fine"); + print!("Here >\u{2060}< is a WJ, and \u{2060}another"); + print!("This\u{2060}is\u{2060}fine"); +} + +#[warn(clippy::unicode_not_nfc)] +fn canon() { + print!("̀àh?"); + print!("a\u{0300}h?"); // also ok +} + +#[warn(clippy::non_ascii_literal)] +fn uni() { + print!("\u{dc}ben!"); + print!("\u{DC}ben!"); // this is ok +} + +// issue 8013 +#[warn(clippy::non_ascii_literal)] +fn single_quote() { + const _EMPTY_BLOCK: char = '\u{25b1}'; + const _FULL_BLOCK: char = '\u{25b0}'; +} + +fn main() { + zero(); + uni(); + canon(); + single_quote(); +} diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index e0a4eadce33..7828d6bcbea 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -1,3 +1,4 @@ +// run-rustfix #[warn(clippy::invisible_characters)] fn zero() { print!("Here >​< is a ZWS, and ​another"); diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index 3f54e3880e7..01d3f3c0296 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -1,5 +1,5 @@ error: invisible character detected - --> $DIR/unicode.rs:3:12 + --> $DIR/unicode.rs:4:12 | LL | print!("Here >​< is a ZWS, and ​another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"` @@ -7,19 +7,19 @@ LL | print!("Here >​< is a ZWS, and ​another"); = note: `-D clippy::invisible-characters` implied by `-D warnings` error: invisible character detected - --> $DIR/unicode.rs:5:12 + --> $DIR/unicode.rs:6:12 | LL | print!("Here >­< is a SHY, and ­another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"` error: invisible character detected - --> $DIR/unicode.rs:7:12 + --> $DIR/unicode.rs:8:12 | LL | print!("Here >⁠< is a WJ, and ⁠another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"` error: non-NFC Unicode sequence detected - --> $DIR/unicode.rs:13:12 + --> $DIR/unicode.rs:14:12 | LL | print!("̀àh?"); | ^^^^^ help: consider replacing the string with: `"̀àh?"` @@ -27,7 +27,7 @@ LL | print!("̀àh?"); = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:19:12 + --> $DIR/unicode.rs:20:12 | LL | print!("Üben!"); | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` @@ -35,13 +35,13 @@ LL | print!("Üben!"); = note: `-D clippy::non-ascii-literal` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:26:32 + --> $DIR/unicode.rs:27:32 | LL | const _EMPTY_BLOCK: char = '▱'; | ^^^ help: consider replacing the string with: `'/u{25b1}'` error: literal non-ASCII character detected - --> $DIR/unicode.rs:27:31 + --> $DIR/unicode.rs:28:31 | LL | const _FULL_BLOCK: char = '▰'; | ^^^ help: consider replacing the string with: `'/u{25b0}'` diff --git a/tests/ui/unit_arg_empty_blocks.fixed b/tests/ui/unit_arg_empty_blocks.fixed new file mode 100644 index 00000000000..9400e93cac8 --- /dev/null +++ b/tests/ui/unit_arg_empty_blocks.fixed @@ -0,0 +1,30 @@ +// run-rustfix +#![warn(clippy::unit_arg)] +#![allow(clippy::no_effect, unused_must_use, unused_variables)] + +use std::fmt::Debug; + +fn foo(t: T) { + println!("{:?}", t); +} + +fn foo3(t1: T1, t2: T2, t3: T3) { + println!("{:?}, {:?}, {:?}", t1, t2, t3); +} + +fn bad() { + foo(()); + foo3((), 2, 2); + foo(0); + taking_two_units((), ()); + foo(0); + foo(1); + taking_three_units((), (), ()); +} + +fn taking_two_units(a: (), b: ()) {} +fn taking_three_units(a: (), b: (), c: ()) {} + +fn main() { + bad(); +} diff --git a/tests/ui/unit_arg_empty_blocks.rs b/tests/ui/unit_arg_empty_blocks.rs index 18a31eb3dee..5f52b6c5315 100644 --- a/tests/ui/unit_arg_empty_blocks.rs +++ b/tests/ui/unit_arg_empty_blocks.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::unit_arg)] #![allow(clippy::no_effect, unused_must_use, unused_variables)] diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index 39072c9a8cc..d35e931697d 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg_empty_blocks.rs:15:5 + --> $DIR/unit_arg_empty_blocks.rs:16:5 | LL | foo({}); | ^^^^--^ @@ -9,7 +9,7 @@ LL | foo({}); = note: `-D clippy::unit-arg` implied by `-D warnings` error: passing a unit value to a function - --> $DIR/unit_arg_empty_blocks.rs:16:5 + --> $DIR/unit_arg_empty_blocks.rs:17:5 | LL | foo3({}, 2, 2); | ^^^^^--^^^^^^^ @@ -17,7 +17,7 @@ LL | foo3({}, 2, 2); | help: use a unit literal instead: `()` error: passing unit values to a function - --> $DIR/unit_arg_empty_blocks.rs:17:5 + --> $DIR/unit_arg_empty_blocks.rs:18:5 | LL | taking_two_units({}, foo(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL ~ taking_two_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg_empty_blocks.rs:18:5 + --> $DIR/unit_arg_empty_blocks.rs:19:5 | LL | taking_three_units({}, foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed new file mode 100644 index 00000000000..b352b285c86 --- /dev/null +++ b/tests/ui/unnecessary_cast.fixed @@ -0,0 +1,91 @@ +// run-rustfix +#![warn(clippy::unnecessary_cast)] +#![allow( + unused_must_use, + clippy::borrow_as_ptr, + clippy::no_effect, + clippy::nonstandard_macro_braces, + clippy::unnecessary_operation +)] + +#[rustfmt::skip] +fn main() { + // Test cast_unnecessary + 1_i32; + 1_f32; + false; + &1i32 as &i32; + + -1_i32; + - 1_i32; + -1_f32; + 1_i32; + 1_f32; + + // macro version + macro_rules! foo { + ($a:ident, $b:ident) => { + #[allow(unused)] + pub fn $a() -> $b { + 1 as $b + } + }; + } + foo!(a, i32); + foo!(b, f32); + foo!(c, f64); + + // do not lint cast to cfg-dependant type + 1 as std::os::raw::c_char; + + // do not lint cast to alias type + 1 as I32Alias; + &1 as &I32Alias; +} + +type I32Alias = i32; + +mod fixable { + #![allow(dead_code)] + + fn main() { + // casting integer literal to float is unnecessary + 100_f32; + 100_f64; + 100_f64; + let _ = -100_f32; + let _ = -100_f64; + let _ = -100_f64; + 100_f32; + 100_f64; + // Should not trigger + #[rustfmt::skip] + let v = vec!(1); + &v as &[i32]; + 0x10 as f32; + 0o10 as f32; + 0b10 as f32; + 0x11 as f64; + 0o11 as f64; + 0b11 as f64; + + 1_u32; + 0x10_i32; + 0b10_usize; + 0o73_u16; + 1_000_000_000_u32; + + 1.0_f64; + 0.5_f32; + + 1.0 as u16; + + let _ = -1_i32; + let _ = -1.0_f32; + + let _ = 1 as I32Alias; + let _ = &1 as &I32Alias; + } + + type I32Alias = i32; +} diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 62c3e963686..6c8cc3effe8 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -1,5 +1,12 @@ +// run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect)] +#![allow( + unused_must_use, + clippy::borrow_as_ptr, + clippy::no_effect, + clippy::nonstandard_macro_braces, + clippy::unnecessary_operation +)] #[rustfmt::skip] fn main() { @@ -37,3 +44,48 @@ fn main() { } type I32Alias = i32; + +mod fixable { + #![allow(dead_code)] + + fn main() { + // casting integer literal to float is unnecessary + 100 as f32; + 100 as f64; + 100_i32 as f64; + let _ = -100 as f32; + let _ = -100 as f64; + let _ = -100_i32 as f64; + 100. as f32; + 100. as f64; + // Should not trigger + #[rustfmt::skip] + let v = vec!(1); + &v as &[i32]; + 0x10 as f32; + 0o10 as f32; + 0b10 as f32; + 0x11 as f64; + 0o11 as f64; + 0b11 as f64; + + 1 as u32; + 0x10 as i32; + 0b10 as usize; + 0o73 as u16; + 1_000_000_000 as u32; + + 1.0 as f64; + 0.5 as f32; + + 1.0 as u16; + + let _ = -1 as i32; + let _ = -1.0 as f32; + + let _ = 1 as I32Alias; + let _ = &1 as &I32Alias; + } + + type I32Alias = i32; +} diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index a5a93c6110c..bad45f0025b 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -1,5 +1,5 @@ error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:7:5 + --> $DIR/unnecessary_cast.rs:14:5 | LL | 1i32 as i32; | ^^^^^^^^^^^ help: try: `1_i32` @@ -7,46 +7,148 @@ LL | 1i32 as i32; = note: `-D clippy::unnecessary-cast` implied by `-D warnings` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:8:5 + --> $DIR/unnecessary_cast.rs:15:5 | LL | 1f32 as f32; | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) - --> $DIR/unnecessary_cast.rs:9:5 + --> $DIR/unnecessary_cast.rs:16:5 | LL | false as bool; | ^^^^^^^^^^^^^ help: try: `false` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:12:5 + --> $DIR/unnecessary_cast.rs:19:5 | LL | -1_i32 as i32; | ^^^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:13:5 + --> $DIR/unnecessary_cast.rs:20:5 | LL | - 1_i32 as i32; | ^^^^^^^^^^^^^^ help: try: `- 1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:14:5 + --> $DIR/unnecessary_cast.rs:21:5 | LL | -1f32 as f32; | ^^^^^^^^^^^^ help: try: `-1_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:15:5 + --> $DIR/unnecessary_cast.rs:22:5 | LL | 1_i32 as i32; | ^^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:16:5 + --> $DIR/unnecessary_cast.rs:23:5 | LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` -error: aborting due to 8 previous errors +error: casting integer literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:53:9 + | +LL | 100 as f32; + | ^^^^^^^^^^ help: try: `100_f32` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:54:9 + | +LL | 100 as f64; + | ^^^^^^^^^^ help: try: `100_f64` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:55:9 + | +LL | 100_i32 as f64; + | ^^^^^^^^^^^^^^ help: try: `100_f64` + +error: casting integer literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:56:17 + | +LL | let _ = -100 as f32; + | ^^^^^^^^^^^ help: try: `-100_f32` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:57:17 + | +LL | let _ = -100 as f64; + | ^^^^^^^^^^^ help: try: `-100_f64` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:58:17 + | +LL | let _ = -100_i32 as f64; + | ^^^^^^^^^^^^^^^ help: try: `-100_f64` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:59:9 + | +LL | 100. as f32; + | ^^^^^^^^^^^ help: try: `100_f32` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:60:9 + | +LL | 100. as f64; + | ^^^^^^^^^^^ help: try: `100_f64` + +error: casting integer literal to `u32` is unnecessary + --> $DIR/unnecessary_cast.rs:72:9 + | +LL | 1 as u32; + | ^^^^^^^^ help: try: `1_u32` + +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast.rs:73:9 + | +LL | 0x10 as i32; + | ^^^^^^^^^^^ help: try: `0x10_i32` + +error: casting integer literal to `usize` is unnecessary + --> $DIR/unnecessary_cast.rs:74:9 + | +LL | 0b10 as usize; + | ^^^^^^^^^^^^^ help: try: `0b10_usize` + +error: casting integer literal to `u16` is unnecessary + --> $DIR/unnecessary_cast.rs:75:9 + | +LL | 0o73 as u16; + | ^^^^^^^^^^^ help: try: `0o73_u16` + +error: casting integer literal to `u32` is unnecessary + --> $DIR/unnecessary_cast.rs:76:9 + | +LL | 1_000_000_000 as u32; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:78:9 + | +LL | 1.0 as f64; + | ^^^^^^^^^^ help: try: `1.0_f64` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:79:9 + | +LL | 0.5 as f32; + | ^^^^^^^^^^ help: try: `0.5_f32` + +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast.rs:83:17 + | +LL | let _ = -1 as i32; + | ^^^^^^^^^ help: try: `-1_i32` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:84:17 + | +LL | let _ = -1.0 as f32; + | ^^^^^^^^^^^ help: try: `-1.0_f32` + +error: aborting due to 25 previous errors diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed deleted file mode 100644 index 36800c5340d..00000000000 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ /dev/null @@ -1,50 +0,0 @@ -// run-rustfix - -#![warn(clippy::unnecessary_cast)] -#![allow( - clippy::no_effect, - clippy::unnecessary_operation, - clippy::nonstandard_macro_braces, - clippy::borrow_as_ptr -)] - -fn main() { - // casting integer literal to float is unnecessary - 100_f32; - 100_f64; - 100_f64; - let _ = -100_f32; - let _ = -100_f64; - let _ = -100_f64; - 100_f32; - 100_f64; - // Should not trigger - #[rustfmt::skip] - let v = vec!(1); - &v as &[i32]; - 0x10 as f32; - 0o10 as f32; - 0b10 as f32; - 0x11 as f64; - 0o11 as f64; - 0b11 as f64; - - 1_u32; - 0x10_i32; - 0b10_usize; - 0o73_u16; - 1_000_000_000_u32; - - 1.0_f64; - 0.5_f32; - - 1.0 as u16; - - let _ = -1_i32; - let _ = -1.0_f32; - - let _ = 1 as I32Alias; - let _ = &1 as &I32Alias; -} - -type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs deleted file mode 100644 index d4b6bb952ab..00000000000 --- a/tests/ui/unnecessary_cast_fixable.rs +++ /dev/null @@ -1,50 +0,0 @@ -// run-rustfix - -#![warn(clippy::unnecessary_cast)] -#![allow( - clippy::no_effect, - clippy::unnecessary_operation, - clippy::nonstandard_macro_braces, - clippy::borrow_as_ptr -)] - -fn main() { - // casting integer literal to float is unnecessary - 100 as f32; - 100 as f64; - 100_i32 as f64; - let _ = -100 as f32; - let _ = -100 as f64; - let _ = -100_i32 as f64; - 100. as f32; - 100. as f64; - // Should not trigger - #[rustfmt::skip] - let v = vec!(1); - &v as &[i32]; - 0x10 as f32; - 0o10 as f32; - 0b10 as f32; - 0x11 as f64; - 0o11 as f64; - 0b11 as f64; - - 1 as u32; - 0x10 as i32; - 0b10 as usize; - 0o73 as u16; - 1_000_000_000 as u32; - - 1.0 as f64; - 0.5 as f32; - - 1.0 as u16; - - let _ = -1 as i32; - let _ = -1.0 as f32; - - let _ = 1 as I32Alias; - let _ = &1 as &I32Alias; -} - -type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr deleted file mode 100644 index a281143281b..00000000000 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ /dev/null @@ -1,106 +0,0 @@ -error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:13:5 - | -LL | 100 as f32; - | ^^^^^^^^^^ help: try: `100_f32` - | - = note: `-D clippy::unnecessary-cast` implied by `-D warnings` - -error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:14:5 - | -LL | 100 as f64; - | ^^^^^^^^^^ help: try: `100_f64` - -error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:15:5 - | -LL | 100_i32 as f64; - | ^^^^^^^^^^^^^^ help: try: `100_f64` - -error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:16:13 - | -LL | let _ = -100 as f32; - | ^^^^^^^^^^^ help: try: `-100_f32` - -error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:17:13 - | -LL | let _ = -100 as f64; - | ^^^^^^^^^^^ help: try: `-100_f64` - -error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:18:13 - | -LL | let _ = -100_i32 as f64; - | ^^^^^^^^^^^^^^^ help: try: `-100_f64` - -error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:19:5 - | -LL | 100. as f32; - | ^^^^^^^^^^^ help: try: `100_f32` - -error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:20:5 - | -LL | 100. as f64; - | ^^^^^^^^^^^ help: try: `100_f64` - -error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:32:5 - | -LL | 1 as u32; - | ^^^^^^^^ help: try: `1_u32` - -error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:33:5 - | -LL | 0x10 as i32; - | ^^^^^^^^^^^ help: try: `0x10_i32` - -error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:34:5 - | -LL | 0b10 as usize; - | ^^^^^^^^^^^^^ help: try: `0b10_usize` - -error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:35:5 - | -LL | 0o73 as u16; - | ^^^^^^^^^^^ help: try: `0o73_u16` - -error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:36:5 - | -LL | 1_000_000_000 as u32; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` - -error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:38:5 - | -LL | 1.0 as f64; - | ^^^^^^^^^^ help: try: `1.0_f64` - -error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:39:5 - | -LL | 0.5 as f32; - | ^^^^^^^^^^ help: try: `0.5_f32` - -error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:43:13 - | -LL | let _ = -1 as i32; - | ^^^^^^^^^ help: try: `-1_i32` - -error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:44:13 - | -LL | let _ = -1.0 as f32; - | ^^^^^^^^^^^ help: try: `-1.0_f32` - -error: aborting due to 17 previous errors - diff --git a/tests/ui/unused_rounding.fixed b/tests/ui/unused_rounding.fixed new file mode 100644 index 00000000000..54f85806ac3 --- /dev/null +++ b/tests/ui/unused_rounding.fixed @@ -0,0 +1,9 @@ +// run-rustfix +#![warn(clippy::unused_rounding)] + +fn main() { + let _ = 1f32; + let _ = 1.0f64; + let _ = 1.00f32; + let _ = 2e-54f64.floor(); +} diff --git a/tests/ui/unused_rounding.rs b/tests/ui/unused_rounding.rs new file mode 100644 index 00000000000..8d007bc4a1d --- /dev/null +++ b/tests/ui/unused_rounding.rs @@ -0,0 +1,9 @@ +// run-rustfix +#![warn(clippy::unused_rounding)] + +fn main() { + let _ = 1f32.ceil(); + let _ = 1.0f64.floor(); + let _ = 1.00f32.round(); + let _ = 2e-54f64.floor(); +} diff --git a/tests/ui/unused_rounding.stderr b/tests/ui/unused_rounding.stderr new file mode 100644 index 00000000000..6cfb02e0402 --- /dev/null +++ b/tests/ui/unused_rounding.stderr @@ -0,0 +1,22 @@ +error: used the `ceil` method with a whole number float + --> $DIR/unused_rounding.rs:5:13 + | +LL | let _ = 1f32.ceil(); + | ^^^^^^^^^^^ help: remove the `ceil` method call: `1f32` + | + = note: `-D clippy::unused-rounding` implied by `-D warnings` + +error: used the `floor` method with a whole number float + --> $DIR/unused_rounding.rs:6:13 + | +LL | let _ = 1.0f64.floor(); + | ^^^^^^^^^^^^^^ help: remove the `floor` method call: `1.0f64` + +error: used the `round` method with a whole number float + --> $DIR/unused_rounding.rs:7:13 + | +LL | let _ = 1.00f32.round(); + | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `1.00f32` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 3e62ffe74fe..4f80aaecc90 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -542,3 +542,69 @@ mod use_self_in_pat { } } } + +mod issue8845 { + pub enum Something { + Num(u8), + TupleNums(u8, u8), + StructNums { one: u8, two: u8 }, + } + + struct Foo(u8); + + struct Bar { + x: u8, + y: usize, + } + + impl Something { + fn get_value(&self) -> u8 { + match self { + Self::Num(n) => *n, + Self::TupleNums(n, _m) => *n, + Self::StructNums { one, two: _ } => *one, + } + } + + fn use_crate(&self) -> u8 { + match self { + Self::Num(n) => *n, + Self::TupleNums(n, _m) => *n, + Self::StructNums { one, two: _ } => *one, + } + } + + fn imported_values(&self) -> u8 { + use Something::*; + match self { + Num(n) => *n, + TupleNums(n, _m) => *n, + StructNums { one, two: _ } => *one, + } + } + } + + impl Foo { + fn get_value(&self) -> u8 { + let Self(x) = self; + *x + } + + fn use_crate(&self) -> u8 { + let Self(x) = self; + *x + } + } + + impl Bar { + fn get_value(&self) -> u8 { + let Self { x, .. } = self; + *x + } + + fn use_crate(&self) -> u8 { + let Self { x, .. } = self; + *x + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index da2faddee12..52da72db53c 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -542,3 +542,69 @@ mod use_self_in_pat { } } } + +mod issue8845 { + pub enum Something { + Num(u8), + TupleNums(u8, u8), + StructNums { one: u8, two: u8 }, + } + + struct Foo(u8); + + struct Bar { + x: u8, + y: usize, + } + + impl Something { + fn get_value(&self) -> u8 { + match self { + Something::Num(n) => *n, + Something::TupleNums(n, _m) => *n, + Something::StructNums { one, two: _ } => *one, + } + } + + fn use_crate(&self) -> u8 { + match self { + crate::issue8845::Something::Num(n) => *n, + crate::issue8845::Something::TupleNums(n, _m) => *n, + crate::issue8845::Something::StructNums { one, two: _ } => *one, + } + } + + fn imported_values(&self) -> u8 { + use Something::*; + match self { + Num(n) => *n, + TupleNums(n, _m) => *n, + StructNums { one, two: _ } => *one, + } + } + } + + impl Foo { + fn get_value(&self) -> u8 { + let Foo(x) = self; + *x + } + + fn use_crate(&self) -> u8 { + let crate::issue8845::Foo(x) = self; + *x + } + } + + impl Bar { + fn get_value(&self) -> u8 { + let Bar { x, .. } = self; + *x + } + + fn use_crate(&self) -> u8 { + let crate::issue8845::Bar { x, .. } = self; + *x + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 34d98618253..f06bb959b3b 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -186,5 +186,65 @@ error: unnecessary structure name repetition LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` -error: aborting due to 31 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:563:17 + | +LL | Something::Num(n) => *n, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:564:17 + | +LL | Something::TupleNums(n, _m) => *n, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:565:17 + | +LL | Something::StructNums { one, two: _ } => *one, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:571:17 + | +LL | crate::issue8845::Something::Num(n) => *n, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:572:17 + | +LL | crate::issue8845::Something::TupleNums(n, _m) => *n, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:573:17 + | +LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:589:17 + | +LL | let Foo(x) = self; + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:594:17 + | +LL | let crate::issue8845::Foo(x) = self; + | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:601:17 + | +LL | let Bar { x, .. } = self; + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:606:17 + | +LL | let crate::issue8845::Bar { x, .. } = self; + | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 41 previous errors diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index bf4ce79b2cb..6909fbcae09 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -232,6 +232,9 @@ return true; } searchStr = searchStr.toLowerCase(); + if (searchStr.startsWith("clippy::")) { + searchStr = searchStr.slice(8); + } // Search by id if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) { @@ -343,17 +346,23 @@ function setTheme(theme, store) { let enableNight = false; let enableAyu = false; - if (theme == "ayu") { - enableAyu = true; - } else if (theme == "coal" || theme == "navy") { - enableNight = true; - } else if (theme == "rust") { - enableHighlight = true; - } else { - enableHighlight = true; - // this makes sure that an unknown theme request gets set to a known one - theme = "light"; + switch(theme) { + case "ayu": + enableAyu = true; + break; + case "coal": + case "navy": + enableNight = true; + break; + case "rust": + enableHighlight = true; + break; + default: + enableHighlight = true; + theme = "light"; + break; } + document.getElementsByTagName("body")[0].className = theme; document.getElementById("styleHighlight").disabled = !enableHighlight; @@ -368,4 +377,10 @@ function setTheme(theme, store) { } // loading the theme after the initial load -setTheme(localStorage.getItem('clippy-lint-list-theme'), false); +const prefersDark = window.matchMedia("(prefers-color-scheme: dark)"); +const theme = localStorage.getItem('clippy-lint-list-theme'); +if (prefersDark.matches && !theme) { + setTheme("coal", false); +} else { + setTheme(theme, false); +}