diff --git a/CHANGELOG.md b/CHANGELOG.md index 920d397ad..2278a8dc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3437,9 +3437,11 @@ Released 2018-09-13 [`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 +[`arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic [`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 +[`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states [`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 [`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async @@ -3793,6 +3795,7 @@ Released 2018-09-13 [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options [`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref +[`obfuscated_if_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#obfuscated_if_else [`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes [`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect [`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion diff --git a/Cargo.toml b/Cargo.toml index 644ca6318..69deaca0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ compiletest_rs = { version = "0.8", features = ["tmp"] } tester = "0.9" regex = "1.5" toml = "0.5" +walkdir = "2.3" # This is used by the `collect-metadata` alias. filetime = "0.2" @@ -41,6 +42,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" diff --git a/book/src/continuous_integration/github_actions.md b/book/src/continuous_integration/github_actions.md index 42a43ef13..339287a7d 100644 --- a/book/src/continuous_integration/github_actions.md +++ b/book/src/continuous_integration/github_actions.md @@ -1,7 +1,7 @@ # GitHub Actions -On the GitHub hosted runners, Clippy from the latest stable Rust version comes -pre-installed. So all you have to do is to run `cargo clippy`. +GitHub hosted runners using the latest stable version of Rust have Clippy pre-installed. +It is as simple as running `cargo clippy` to run lints against the codebase. ```yml on: push @@ -15,7 +15,7 @@ jobs: clippy_check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Run Clippy run: cargo clippy --all-targets --all-features ``` diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index d06297f2e..da781eb97 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -10,6 +10,10 @@ because that's clearly a non-descriptive name. - [Adding a new lint](#adding-a-new-lint) - [Setup](#setup) - [Getting Started](#getting-started) + - [Defining Our Lint](#defining-our-lint) + - [Standalone](#standalone) + - [Specific Type](#specific-type) + - [Tests Location](#tests-location) - [Testing](#testing) - [Cargo lints](#cargo-lints) - [Rustfix tests](#rustfix-tests) @@ -36,17 +40,38 @@ See the [Basics](basics.md#get-the-code) documentation. ## Getting Started There is a bit of boilerplate code that needs to be set up when creating a new -lint. Fortunately, you can use the clippy dev tools to handle this for you. We +lint. Fortunately, you can use the Clippy dev tools to handle this for you. We are naming our new lint `foo_functions` (lints are generally written in snake -case), and we don't need type information so it will have an early pass type -(more on this later on). If you're not sure if the name you chose fits the lint, -take a look at our [lint naming guidelines][lint_naming]. To get started on this -lint you can run `cargo dev new_lint --name=foo_functions --pass=early ---category=pedantic` (category will default to nursery if not provided). This -command will create two files: `tests/ui/foo_functions.rs` and -`clippy_lints/src/foo_functions.rs`, as well as [registering the -lint](#lint-registration). For cargo lints, two project hierarchies (fail/pass) -will be created by default under `tests/ui-cargo`. +case), and we don't need type information, so it will have an early pass type +(more on this later). If you're unsure if the name you chose fits the lint, +take a look at our [lint naming guidelines][lint_naming]. + +## Defining Our Lint +To get started, there are two ways to define our lint. + +### Standalone +Command: `cargo dev new_lint --name=foo_functions --pass=early --category=pedantic` +(category will default to nursery if not provided) + +This command will create a new file: `clippy_lints/src/foo_functions.rs`, as well +as [register the lint](#lint-registration). + +### Specific Type +Command: `cargo dev new_lint --name=foo_functions --type=functions --category=pedantic` + +This command will create a new file: `clippy_lints/src/{type}/foo_functions.rs`. + +Notice how this command has a `--type` flag instead of `--pass`. Unlike a standalone +definition, this lint won't be registered in the traditional sense. Instead, you will +call your lint from within the type's lint pass, found in `clippy_lints/src/{type}/mod.rs`. + +A "type" is just the name of a directory in `clippy_lints/src`, like `functions` in +the example command. These are groupings of lints with common behaviors, so if your +lint falls into one, it would be best to add it to that type. + +### Tests Location +Both commands will create a file: `tests/ui/foo_functions.rs`. For cargo lints, +two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`. Next, we'll open up these files and add our lint! diff --git a/book/src/development/infrastructure/book.md b/book/src/development/infrastructure/book.md index b62314c67..a48742191 100644 --- a/book/src/development/infrastructure/book.md +++ b/book/src/development/infrastructure/book.md @@ -25,7 +25,7 @@ instructions for other options. ## Make changes The book's -[src](https://github.com/joshrotenberg/rust-clippy/tree/clippy_guide/book/src) +[src](https://github.com/rust-lang/rust-clippy/tree/master/book/src) directory contains all of the markdown files used to generate the book. If you want to see your changes in real time, you can use the mdbook `serve` command to run a web server locally that will automatically update changes as they are diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index d513a229b..3b27f061e 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -13,7 +13,7 @@ pub enum CliError { IoError(io::Error), RustfmtNotInstalled, WalkDirError(walkdir::Error), - RaSetupActive, + IntellijSetupActive, } impl From for CliError { @@ -48,7 +48,7 @@ pub fn run(check: bool, verbose: bool) { .expect("Failed to read clippy Cargo.toml") .contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]") { - return Err(CliError::RaSetupActive); + return Err(CliError::IntellijSetupActive); } rustfmt_test(context)?; @@ -93,11 +93,11 @@ pub fn run(check: bool, verbose: bool) { CliError::WalkDirError(err) => { eprintln!("error: {}", err); }, - CliError::RaSetupActive => { + CliError::IntellijSetupActive => { eprintln!( "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`. Not formatting because that would format the local repo as well! -Please revert the changes to Cargo.tomls first." +Please revert the changes to Cargo.tomls with `cargo dev remove intellij`." ); }, } diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 8536e2429..8a6bd1cbd 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(once_cell)] #![feature(rustc_private)] diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index a29ba2d0c..a417d3dd8 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -36,7 +36,8 @@ fn main() { match new_lint::create( matches.get_one::("pass"), matches.get_one::("name"), - matches.get_one::("category"), + matches.get_one::("category").map(String::as_str), + matches.get_one::("type").map(String::as_str), matches.contains_id("msrv"), ) { Ok(_) => update_lints::update(update_lints::UpdateMode::Change), @@ -157,7 +158,8 @@ fn get_clap_config() -> ArgMatches { .help("Specify whether the lint runs during the early or late pass") .takes_value(true) .value_parser([PossibleValue::new("early"), PossibleValue::new("late")]) - .required(true), + .conflicts_with("type") + .required_unless_present("type"), Arg::new("name") .short('n') .long("name") @@ -183,6 +185,11 @@ fn get_clap_config() -> ArgMatches { PossibleValue::new("internal_warn"), ]) .takes_value(true), + Arg::new("type") + .long("type") + .help("What directory the lint belongs in") + .takes_value(true) + .required(false), Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"), ]), Command::new("setup") diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 7d7e760ef..03d2ef3d1 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,5 +1,5 @@ use crate::clippy_project_root; -use indoc::indoc; +use indoc::{indoc, writedoc}; use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; @@ -10,6 +10,7 @@ struct LintData<'a> { pass: &'a str, name: &'a str, category: &'a str, + ty: Option<&'a str>, project_root: PathBuf, } @@ -37,26 +38,44 @@ impl Context for io::Result { pub fn create( pass: Option<&String>, lint_name: Option<&String>, - category: Option<&String>, + category: Option<&str>, + mut ty: Option<&str>, msrv: bool, ) -> io::Result<()> { + if category == Some("cargo") && ty.is_none() { + // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo` + ty = Some("cargo"); + } + let lint = LintData { - pass: pass.expect("`pass` argument is validated by clap"), + pass: pass.map_or("", String::as_str), name: lint_name.expect("`name` argument is validated by clap"), category: category.expect("`category` argument is validated by clap"), + ty, project_root: clippy_project_root(), }; create_lint(&lint, msrv).context("Unable to create lint implementation")?; create_test(&lint).context("Unable to create a test for the new lint")?; - add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs") + + if lint.ty.is_none() { + add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?; + } + + Ok(()) } fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { - let lint_contents = get_lint_file_contents(lint, enable_msrv); + if let Some(ty) = lint.ty { + create_lint_for_ty(lint, enable_msrv, ty) + } else { + let lint_contents = get_lint_file_contents(lint, enable_msrv); + let lint_path = format!("clippy_lints/src/{}.rs", lint.name); + write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?; + println!("Generated lint file: `{}`", lint_path); - let lint_path = format!("clippy_lints/src/{}.rs", lint.name); - write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes()) + Ok(()) + } } fn create_test(lint: &LintData<'_>) -> io::Result<()> { @@ -75,16 +94,22 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> { if lint.category == "cargo" { let relative_test_dir = format!("tests/ui-cargo/{}", lint.name); - let test_dir = lint.project_root.join(relative_test_dir); + let test_dir = lint.project_root.join(&relative_test_dir); fs::create_dir(&test_dir)?; create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?; - create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint") + create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")?; + + println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`"); } else { let test_path = format!("tests/ui/{}.rs", lint.name); let test_contents = get_test_file_contents(lint.name, None); - write_file(lint.project_root.join(test_path), test_contents) + write_file(lint.project_root.join(&test_path), test_contents)?; + + println!("Generated test file: `{}`", test_path); } + + Ok(()) } fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { @@ -204,7 +229,6 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { }, }; - let version = get_stabilization_version(); let lint_name = lint.name; let category = lint.category; let name_camel = to_camel_case(lint.name); @@ -238,32 +262,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { ) }); - let _ = write!( - result, - indoc! {r#" - declare_clippy_lint! {{ - /// ### What it does - /// - /// ### Why is this bad? - /// - /// ### Example - /// ```rust - /// // example code where clippy issues a warning - /// ``` - /// Use instead: - /// ```rust - /// // example code which does not raise clippy warning - /// ``` - #[clippy::version = "{version}"] - pub {name_upper}, - {category}, - "default lint description" - }} - "#}, - version = version, - name_upper = name_upper, - category = category, - ); + let _ = write!(result, "{}", get_lint_declaration(&name_upper, category)); result.push_str(&if enable_msrv { format!( @@ -312,6 +311,254 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result } +fn get_lint_declaration(name_upper: &str, category: &str) -> String { + format!( + indoc! {r#" + declare_clippy_lint! {{ + /// ### What it does + /// + /// ### Why is this bad? + /// + /// ### Example + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + #[clippy::version = "{version}"] + pub {name_upper}, + {category}, + "default lint description" + }} + "#}, + version = get_stabilization_version(), + name_upper = name_upper, + category = category, + ) +} + +fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::Result<()> { + match ty { + "cargo" => assert_eq!( + lint.category, "cargo", + "Lints of type `cargo` must have the `cargo` category" + ), + _ if lint.category == "cargo" => panic!("Lints of category `cargo` must have the `cargo` type"), + _ => {}, + } + + let ty_dir = lint.project_root.join(format!("clippy_lints/src/{}", ty)); + assert!( + ty_dir.exists() && ty_dir.is_dir(), + "Directory `{}` does not exist!", + ty_dir.display() + ); + + let lint_file_path = ty_dir.join(format!("{}.rs", lint.name)); + assert!( + !lint_file_path.exists(), + "File `{}` already exists", + lint_file_path.display() + ); + + let mod_file_path = ty_dir.join("mod.rs"); + let context_import = setup_mod_file(&mod_file_path, lint)?; + + let name_upper = lint.name.to_uppercase(); + let mut lint_file_contents = String::new(); + + if enable_msrv { + let _ = writedoc!( + lint_file_contents, + r#" + use clippy_utils::{{meets_msrv, msrvs}}; + use rustc_lint::{{{context_import}, LintContext}}; + use rustc_semver::RustcVersion; + + use super::{name_upper}; + + // TODO: Adjust the parameters as necessary + pub(super) fn check(cx: &{context_import}, msrv: Option) {{ + if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{ + return; + }} + todo!(); + }} + "#, + context_import = context_import, + name_upper = name_upper, + ); + } else { + let _ = writedoc!( + lint_file_contents, + r#" + use rustc_lint::{{{context_import}, LintContext}}; + + use super::{name_upper}; + + // TODO: Adjust the parameters as necessary + pub(super) fn check(cx: &{context_import}) {{ + todo!(); + }} + "#, + context_import = context_import, + name_upper = name_upper, + ); + } + + write_file(lint_file_path.as_path(), lint_file_contents)?; + println!("Generated lint file: `clippy_lints/src/{}/{}.rs`", ty, lint.name); + println!( + "Be sure to add a call to `{}::check` in `clippy_lints/src/{}/mod.rs`!", + lint.name, ty + ); + + Ok(()) +} + +#[allow(clippy::too_many_lines)] +fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> { + use super::update_lints::{match_tokens, LintDeclSearchResult}; + use rustc_lexer::TokenKind; + + let lint_name_upper = lint.name.to_uppercase(); + + let mut file_contents = fs::read_to_string(path)?; + assert!( + !file_contents.contains(&lint_name_upper), + "Lint `{}` already defined in `{}`", + lint.name, + path.display() + ); + + let mut offset = 0usize; + let mut last_decl_curly_offset = None; + let mut lint_context = None; + + let mut iter = rustc_lexer::tokenize(&file_contents).map(|t| { + let range = offset..offset + t.len; + offset = range.end; + + LintDeclSearchResult { + token_kind: t.kind, + content: &file_contents[range.clone()], + range, + } + }); + + // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl + while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) { + let mut iter = iter + .by_ref() + .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); + + match content { + "declare_clippy_lint" => { + // matches `!{` + match_tokens!(iter, Bang OpenBrace); + if let Some(LintDeclSearchResult { range, .. }) = + iter.find(|result| result.token_kind == TokenKind::CloseBrace) + { + last_decl_curly_offset = Some(range.end); + } + }, + "impl" => { + let mut token = iter.next(); + match token { + // matches <'foo> + Some(LintDeclSearchResult { + token_kind: TokenKind::Lt, + .. + }) => { + match_tokens!(iter, Lifetime { .. } Gt); + token = iter.next(); + }, + None => break, + _ => {}, + } + + if let Some(LintDeclSearchResult { + token_kind: TokenKind::Ident, + content, + .. + }) = token + { + // Get the appropriate lint context struct + lint_context = match content { + "LateLintPass" => Some("LateContext"), + "EarlyLintPass" => Some("EarlyContext"), + _ => continue, + }; + } + }, + _ => {}, + } + } + + drop(iter); + + let last_decl_curly_offset = + last_decl_curly_offset.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display())); + let lint_context = + lint_context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display())); + + // Add the lint declaration to `mod.rs` + file_contents.replace_range( + // Remove the trailing newline, which should always be present + last_decl_curly_offset..=last_decl_curly_offset, + &format!("\n\n{}", get_lint_declaration(&lint_name_upper, lint.category)), + ); + + // Add the lint to `impl_lint_pass`/`declare_lint_pass` + let impl_lint_pass_start = file_contents.find("impl_lint_pass!").unwrap_or_else(|| { + file_contents + .find("declare_lint_pass!") + .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`/`declare_lint_pass`")) + }); + + let mut arr_start = file_contents[impl_lint_pass_start..].find('[').unwrap_or_else(|| { + panic!("malformed `impl_lint_pass`/`declare_lint_pass`"); + }); + + arr_start += impl_lint_pass_start; + + let mut arr_end = file_contents[arr_start..] + .find(']') + .expect("failed to find `impl_lint_pass` terminator"); + + arr_end += arr_start; + + let mut arr_content = file_contents[arr_start + 1..arr_end].to_string(); + arr_content.retain(|c| !c.is_whitespace()); + + let mut new_arr_content = String::new(); + for ident in arr_content + .split(',') + .chain(std::iter::once(&*lint_name_upper)) + .filter(|s| !s.is_empty()) + { + let _ = write!(new_arr_content, "\n {},", ident); + } + new_arr_content.push('\n'); + + file_contents.replace_range(arr_start + 1..arr_end, &new_arr_content); + + // Just add the mod declaration at the top, it'll be fixed by rustfmt + file_contents.insert_str(0, &format!("mod {};\n", &lint.name)); + + let mut file = OpenOptions::new() + .write(true) + .truncate(true) + .open(path) + .context(format!("trying to open: `{}`", path.display()))?; + file.write_all(file_contents.as_bytes()) + .context(format!("writing to file: `{}`", path.display()))?; + + Ok(lint_context) +} + #[test] fn test_camel_case() { let s = "a_lint"; diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index c089f4d8c..aed38bc28 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -824,10 +824,12 @@ macro_rules! match_tokens { } } -struct LintDeclSearchResult<'a> { - token_kind: TokenKind, - content: &'a str, - range: Range, +pub(crate) use match_tokens; + +pub(crate) struct LintDeclSearchResult<'a> { + pub token_kind: TokenKind, + pub content: &'a str, + pub range: Range, } /// Parse a source file looking for `declare_clippy_lint` macro invocations. diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 79a56dc40..9a3f042ff 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -18,7 +18,7 @@ quine-mc_cluskey = "0.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", optional = true } -tempfile = { version = "3.2", optional = true } +tempfile = { version = "3.3.0", optional = true } toml = "0.5" unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs index 6e5c8f445..c7a76e5f9 100644 --- a/clippy_lints/src/as_conversions.rs +++ b/clippy_lints/src/as_conversions.rs @@ -11,7 +11,7 @@ declare_clippy_lint! { /// Note that this lint is specialized in linting *every single* use of `as` /// regardless of whether good alternatives exist or not. /// If you want more precise lints for `as`, please consider using these separate lints: - /// `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`, + /// `unnecessary_cast`, `cast_lossless/cast_possible_truncation/cast_possible_wrap/cast_precision_loss/cast_sign_loss`, /// `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`. /// There is a good explanation the reason why this lint should work in this way and how it is useful /// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122). diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs new file mode 100644 index 000000000..b6affdee5 --- /dev/null +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -0,0 +1,98 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; +use clippy_utils::path_res; +use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::usage::local_used_after_expr; +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `assert!(r.is_ok())` or `assert!(r.is_err())` calls. + /// + /// ### Why is this bad? + /// An assertion failure cannot output an useful message of the error. + /// + /// ### Example + /// ```rust,ignore + /// # let r = Ok::<_, ()>(()); + /// assert!(r.is_ok()); + /// # let r = Err::<_, ()>(()); + /// assert!(r.is_err()); + /// ``` + #[clippy::version = "1.64.0"] + pub ASSERTIONS_ON_RESULT_STATES, + style, + "`assert!(r.is_ok())`/`assert!(r.is_err())` gives worse error message than directly calling `r.unwrap()`/`r.unwrap_err()`" +} + +declare_lint_pass!(AssertionsOnResultStates => [ASSERTIONS_ON_RESULT_STATES]); + +impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let Some(macro_call) = root_macro_call_first_node(cx, e) + && matches!(cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::assert_macro)) + && let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) + && matches!(panic_expn, PanicExpn::Empty) + && let ExprKind::MethodCall(method_segment, [recv], _) = condition.kind + && let result_type_with_refs = cx.typeck_results().expr_ty(recv) + && let result_type = result_type_with_refs.peel_refs() + && is_type_diagnostic_item(cx, result_type, sym::Result) + && let ty::Adt(_, substs) = result_type.kind() + { + if !is_copy(cx, result_type) { + if result_type_with_refs != result_type { + return; + } else if let Res::Local(binding_id) = path_res(cx, recv) + && local_used_after_expr(cx, binding_id, recv) { + return; + } + } + let mut app = Applicability::MachineApplicable; + match method_segment.ident.as_str() { + "is_ok" if has_debug_impl(cx, substs.type_at(1)) => { + span_lint_and_sugg( + cx, + ASSERTIONS_ON_RESULT_STATES, + macro_call.span, + "called `assert!` with `Result::is_ok`", + "replace with", + format!( + "{}.unwrap()", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 + ), + app, + ); + } + "is_err" if has_debug_impl(cx, substs.type_at(0)) => { + span_lint_and_sugg( + cx, + ASSERTIONS_ON_RESULT_STATES, + macro_call.span, + "called `assert!` with `Result::is_err`", + "replace with", + format!( + "{}.unwrap_err()", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 + ), + app, + ); + } + _ => (), + }; + } + } +} + +/// This checks whether a given type is known to implement Debug. +fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + cx.tcx + .get_diagnostic_item(sym::Debug) + .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) +} diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index abe95c666..9f45db86a 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -1,3 +1,8 @@ +mod common_metadata; +mod feature_name; +mod multiple_crate_versions; +mod wildcard_dependencies; + use cargo_metadata::MetadataCommand; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_lint_allowed; @@ -6,11 +11,6 @@ use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::DUMMY_SP; -mod common_metadata; -mod feature_name; -mod multiple_crate_versions; -mod wildcard_dependencies; - declare_clippy_lint! { /// ### What it does /// Checks to see if all common metadata is defined in diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index 7af200708..7eff71d50 100644 --- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -24,7 +24,10 @@ declare_clippy_lint! { /// Use instead: /// ```rust /// fn is_rust_file(filename: &str) -> bool { - /// filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true) + /// let filename = std::path::Path::new(filename); + /// filename.extension() + /// .map(|ext| ext.eq_ignore_ascii_case("rs")) + /// .unwrap_or(false) /// } /// ``` #[clippy::version = "1.51.0"] diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 8c7cf7748..a90f894a7 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1028,9 +1028,10 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data let mut app = Applicability::MachineApplicable; let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| { + let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee); let sugg = if !snip_is_macro - && expr.precedence().order() < data.position.precedence() && !has_enclosing_paren(&snip) + && (expr.precedence().order() < data.position.precedence() || calls_field) { format!("({})", snip) } else { diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 0aa085fc7..925a8cb8d 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -9,7 +9,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::kw; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does @@ -85,22 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string, _ => false, }; - let sugg = if format_args.format_string_span.contains(value.span) { - // Implicit argument. e.g. `format!("{x}")` span points to `{x}` - let spdata = value.span.data(); - let span = Span::new( - spdata.lo + BytePos(1), - spdata.hi - BytePos(1), - spdata.ctxt, - spdata.parent - ); - let snip = snippet_with_applicability(cx, span, "..", &mut applicability); - if is_new_string { - snip.into() - } else { - format!("{snip}.to_string()") - } - } else if is_new_string { + let sugg = if is_new_string { snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned() } else { let sugg = Sugg::hir_with_applicability(cx, value, "", &mut applicability); diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index ef8be9e87..04b5be6c8 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -141,7 +141,7 @@ fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) { // Get the hir_id of the object we are calling the method on if let ExprKind::MethodCall(path, [ref self_arg, ..], _) = expr.kind; // Is the method to_string() ? - if path.ident.name == sym!(to_string); + if path.ident.name == sym::to_string; // Is the method a part of the ToString trait? (i.e. not to_string() implemented // separately) if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 9afc714b1..5be1c417b 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -6,6 +6,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ 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(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES), LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(attrs::DEPRECATED_CFG_ATTR), @@ -187,6 +188,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::NO_EFFECT_REPLACE), + LintId::of(methods::OBFUSCATED_IF_ELSE), LintId::of(methods::OK_EXPECT), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 91d27bf52..99bde35cf 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -42,6 +42,7 @@ store.register_lints(&[ asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, assertions_on_constants::ASSERTIONS_ON_CONSTANTS, + assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES, async_yields_async::ASYNC_YIELDS_ASYNC, attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON, attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, @@ -330,6 +331,7 @@ store.register_lints(&[ methods::NEEDLESS_SPLITN, methods::NEW_RET_NO_SELF, methods::NO_EFFECT_REPLACE, + methods::OBFUSCATED_IF_ELSE, methods::OK_EXPECT, methods::OPTION_AS_REF_DEREF, methods::OPTION_FILTER_MAP, @@ -417,6 +419,7 @@ store.register_lints(&[ only_used_in_recursion::ONLY_USED_IN_RECURSION, open_options::NONSENSICAL_OPEN_OPTIONS, operators::ABSURD_EXTREME_COMPARISONS, + operators::ARITHMETIC, operators::ASSIGN_OP_PATTERN, operators::BAD_BIT_MASK, operators::CMP_NAN, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 642d62997..f96195299 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -28,6 +28,8 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ 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(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), + LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), 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 a1b546658..4d4b89687 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -86,8 +86,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_ADD_ASSIGN), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(types::LINKEDLIST), LintId::of(types::OPTION_OPTION), diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 43f1c892e..495abd838 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -48,6 +48,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION), LintId::of(module_style::MOD_MODULE_FILES), LintId::of(module_style::SELF_NAMED_MODULE_FILES), + LintId::of(operators::ARITHMETIC), LintId::of(operators::FLOAT_ARITHMETIC), LintId::of(operators::FLOAT_CMP_CONST), LintId::of(operators::INTEGER_ARITHMETIC), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 15a1bc569..e029a5235 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -4,6 +4,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), + LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES), LintId::of(blacklisted_name::BLACKLISTED_NAME), LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), @@ -70,6 +71,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::NEW_RET_NO_SELF), + LintId::of(methods::OBFUSCATED_IF_ELSE), LintId::of(methods::OK_EXPECT), LintId::of(methods::OPTION_MAP_OR_NONE), LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1988c2457..197c415f2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -4,7 +4,6 @@ #![feature(control_flow_enum)] #![feature(drain_filter)] #![feature(iter_intersperse)] -#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(lint_reasons)] #![feature(never_type)] @@ -174,6 +173,7 @@ mod as_conversions; mod as_underscore; mod asm_syntax; mod assertions_on_constants; +mod assertions_on_result_states; mod async_yields_async; mod attrs; mod await_holding_invalid; @@ -548,6 +548,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl)); } + let arithmetic_allowed = conf.arithmetic_allowed.clone(); + store.register_late_pass(move || Box::new(operators::arithmetic::Arithmetic::new(arithmetic_allowed.clone()))); store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|| Box::new(utils::author::Author)); let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); @@ -727,6 +729,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy)); store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api))); store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants)); + store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates)); store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull)); store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite)); store.register_late_pass(|| Box::new(inherent_to_string::InherentToString)); @@ -782,7 +785,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(|| Box::new(default::Default::default())); - store.register_late_pass(|| Box::new(unused_self::UnusedSelf)); + store.register_late_pass(move || Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api))); store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|| Box::new(exit::Exit)); store.register_late_pass(|| Box::new(to_digit_is_some::ToDigitIsSome)); @@ -916,7 +919,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); - store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports)); + store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default())); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 3f69cc203..573a7c016 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -460,7 +460,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { let mut sub_visitor = RefVisitor::new(self.cx); sub_visitor.visit_fn_decl(decl); self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); - return; }, TyKind::TraitObject(bounds, ref lt, _) => { if !lt.is_elided() { @@ -469,7 +468,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { for bound in bounds { self.visit_poly_trait_ref(bound, TraitBoundModifier::None); } - return; }, _ => walk_ty(self, ty), } diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index ca617859d..45af6be26 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -11,7 +11,7 @@ use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) { ([stmt, stmts @ ..], expr) => { - if let StmtKind::Local(&Local { init: Some(e), els: None, .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind { + if let StmtKind::Local(&Local { init: Some(e), .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind { (e, !stmts.is_empty() || expr.is_some()) } else { return; diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index d573a1b4f..6b78c1620 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -35,7 +35,8 @@ struct PathAndSpan { span: Span, } -/// `MacroRefData` includes the name of the macro. +/// `MacroRefData` includes the name of the macro +/// and the path from `SourceMap::span_to_filename`. #[derive(Debug, Clone)] pub struct MacroRefData { name: String, diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index d55082c66..b638f2716 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,13 +1,3 @@ -use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context}; -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, 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; @@ -31,6 +21,16 @@ mod single_match; mod try_err; mod wild_in_or_pats; +use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context}; +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, 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}; + declare_clippy_lint! { /// ### What it does /// Checks for matches with a single arm where an `if let` @@ -793,18 +793,13 @@ declare_clippy_lint! { /// ### 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() { @@ -815,22 +810,17 @@ declare_clippy_lint! { /// }; /// /// 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(); diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 06ead144a..f52170df6 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -14,7 +14,7 @@ use super::INEFFICIENT_TO_STRING; /// Checks for the `INEFFICIENT_TO_STRING` lint pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { if_chain! { - if args.len() == 1 && method_name == sym!(to_string); + if args.len() == 1 && method_name == sym::to_string; if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD); if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6981b4a66..202fbc1f7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -46,6 +46,7 @@ mod map_unwrap_or; mod needless_option_as_deref; mod needless_option_take; mod no_effect_replace; +mod obfuscated_if_else; mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; @@ -2263,6 +2264,35 @@ declare_clippy_lint! { "replace with no effect" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `.then_some(..).unwrap_or(..)` + /// + /// ### Why is this bad? + /// This can be written more clearly with `if .. else ..` + /// + /// ### Limitations + /// This lint currently only looks for usages of + /// `.then_some(..).unwrap_or(..)`, but will be expanded + /// to account for similar patterns. + /// + /// ### Example + /// ```rust + /// let x = true; + /// x.then_some("a").unwrap_or("b"); + /// ``` + /// Use instead: + /// ```rust + /// let x = true; + /// if x { "a" } else { "b" }; + /// ``` + #[clippy::version = "1.64.0"] + pub OBFUSCATED_IF_ELSE, + style, + "use of `.then_some(..).unwrap_or(..)` can be written \ + more clearly with `if .. else ..`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2364,6 +2394,7 @@ impl_lint_pass!(Methods => [ IS_DIGIT_ASCII_RADIX, NEEDLESS_OPTION_TAKE, NO_EFFECT_REPLACE, + OBFUSCATED_IF_ELSE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2772,6 +2803,9 @@ impl Methods { Some(("map", [m_recv, m_arg], span)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); }, + Some(("then_some", [t_recv, t_arg], _)) => { + obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg); + }, _ => {}, }, ("unwrap_or_else", [u_arg]) => match method_call(recv) { diff --git a/clippy_lints/src/methods/obfuscated_if_else.rs b/clippy_lints/src/methods/obfuscated_if_else.rs new file mode 100644 index 000000000..4d7427b26 --- /dev/null +++ b/clippy_lints/src/methods/obfuscated_if_else.rs @@ -0,0 +1,42 @@ +// run-rustfix + +use super::OBFUSCATED_IF_ELSE; +use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_with_applicability}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + then_recv: &'tcx hir::Expr<'_>, + then_arg: &'tcx hir::Expr<'_>, + unwrap_arg: &'tcx hir::Expr<'_>, +) { + // something.then_some(blah).unwrap_or(blah) + // ^^^^^^^^^-then_recv ^^^^-then_arg ^^^^- unwrap_arg + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr + + let recv_ty = cx.typeck_results().expr_ty(then_recv); + + if recv_ty.is_bool() { + let mut applicability = Applicability::MachineApplicable; + let sugg = format!( + "if {} {{ {} }} else {{ {} }}", + snippet_with_applicability(cx, then_recv.span, "..", &mut applicability), + snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), + snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability) + ); + + span_lint_and_sugg( + cx, + OBFUSCATED_IF_ELSE, + expr.span, + "use of `.then_some(..).unwrap_or(..)` can be written \ + more clearly with `if .. else ..`", + "try", + sugg, + applicability, + ); + } +} diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index 254d9a700..f763e0d24 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -18,6 +18,11 @@ declare_clippy_lint! { /// Naming type parameters inconsistently may cause you to refer to the /// wrong type parameter. /// + /// ### Limitations + /// This lint only applies to impl blocks with simple generic params, e.g. + /// `A`. If there is anything more complicated, such as a tuple, it will be + /// ignored. + /// /// ### Example /// ```rust /// struct Foo { @@ -53,14 +58,15 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { 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| + let mut impl_params = Vec::new(); + for p in generic_args.args.iter() { match p { GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) => - Some((path.segments[0].ident.to_string(), path.span)), - _ => None, - } - ); + impl_params.push((path.segments[0].ident.to_string(), path.span)), + GenericArg::Type(_) => return, + _ => (), + }; + } // find the type that the Impl is for // only lint on struct/enum/union for now @@ -83,8 +89,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { 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) { + for (i, (impl_param_name, impl_param_span)) in impl_params.iter().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", @@ -92,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { span_lint_and_help( cx, MISMATCHING_TYPE_PARAM_ORDER, - impl_param_span, + *impl_param_span, &msg, None, &help diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index a20377f32..b99052e66 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -7,7 +7,8 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast; +use if_chain::if_chain; +use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, DefIdTree}; @@ -57,6 +58,20 @@ impl MissingDoc { *self.doc_hidden_stack.last().expect("empty doc_hidden_stack") } + fn has_include(meta: Option) -> bool { + if_chain! { + if let Some(meta) = meta; + if let MetaItemKind::List(list) = meta.kind; + if let Some(meta) = list.get(0); + if let Some(name) = meta.ident(); + then { + name.name == sym::include + } else { + false + } + } + } + fn check_missing_docs_attrs( &self, cx: &LateContext<'_>, @@ -80,7 +95,9 @@ impl MissingDoc { return; } - let has_doc = attrs.iter().any(|a| a.doc_str().is_some()); + let has_doc = attrs + .iter() + .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())); if !has_doc { span_lint( cx, diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 44fdf84c6..321db08df 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -95,10 +95,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> { self.found = true; return; }, - ExprKind::If(..) => { - self.found = true; - return; - }, ExprKind::Path(_) => { if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { if adj diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 6bce5fbd4..72c86f28b 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -251,14 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { if let ItemKind::Const(hir_ty, body_id) = it.kind { let ty = hir_ty_to_ty(cx.tcx, hir_ty); - if !macro_backtrace(it.span).last().map_or(false, |macro_call| { - matches!( - cx.tcx.get_diagnostic_name(macro_call.def_id), - Some(sym::thread_local_macro) - ) - }) && is_unfrozen(cx, ty) - && is_value_unfrozen_poly(cx, body_id, ty) - { + if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) { lint(cx, Source::Item { item: it.span }); } } @@ -445,3 +438,12 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } } } + +fn ignored_macro(cx: &LateContext<'_>, it: &rustc_hir::Item<'_>) -> bool { + macro_backtrace(it.span).any(|macro_call| { + matches!( + cx.tcx.get_diagnostic_name(macro_call.def_id), + Some(sym::thread_local_macro) + ) + }) +} diff --git a/clippy_lints/src/operators/arithmetic.rs b/clippy_lints/src/operators/arithmetic.rs new file mode 100644 index 000000000..800cf249f --- /dev/null +++ b/clippy_lints/src/operators/arithmetic.rs @@ -0,0 +1,119 @@ +#![allow( + // False positive + clippy::match_same_arms +)] + +use super::ARITHMETIC; +use clippy_utils::{consts::constant_simple, diagnostics::span_lint}; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::source_map::Span; + +const HARD_CODED_ALLOWED: &[&str] = &["std::num::Saturating", "std::string::String", "std::num::Wrapping"]; + +#[derive(Debug)] +pub struct Arithmetic { + allowed: FxHashSet, + // Used to check whether expressions are constants, such as in enum discriminants and consts + const_span: Option, + expr_span: Option, +} + +impl_lint_pass!(Arithmetic => [ARITHMETIC]); + +impl Arithmetic { + #[must_use] + pub fn new(mut allowed: FxHashSet) -> Self { + allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from)); + Self { + allowed, + const_span: None, + expr_span: None, + } + } + + /// Checks if the given `expr` has any of the inner `allowed` elements. + fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + self.allowed.contains( + cx.typeck_results() + .expr_ty(expr) + .to_string() + .split('<') + .next() + .unwrap_or_default(), + ) + } + + fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { + span_lint(cx, ARITHMETIC, expr.span, "arithmetic detected"); + self.expr_span = Some(expr.span); + } +} + +impl<'tcx> LateLintPass<'tcx> for Arithmetic { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if self.expr_span.is_some() { + return; + } + if let Some(span) = self.const_span && span.contains(expr.span) { + return; + } + match &expr.kind { + hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => { + let ( + hir::BinOpKind::Add + | hir::BinOpKind::Sub + | hir::BinOpKind::Mul + | hir::BinOpKind::Div + | hir::BinOpKind::Rem + | hir::BinOpKind::Shl + | hir::BinOpKind::Shr + ) = op.node else { + return; + }; + if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) { + return; + } + self.issue_lint(cx, expr); + }, + hir::ExprKind::Unary(hir::UnOp::Neg, _) => { + // CTFE already takes care of things like `-1` that do not overflow. + if constant_simple(cx, cx.typeck_results(), expr).is_none() { + self.issue_lint(cx, expr); + } + }, + _ => {}, + } + } + + fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { + let body_owner = cx.tcx.hir().body_owner_def_id(body.id()); + match cx.tcx.hir().body_owner_kind(body_owner) { + hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => { + let body_span = cx.tcx.def_span(body_owner); + if let Some(span) = self.const_span && span.contains(body_span) { + return; + } + self.const_span = Some(body_span); + }, + hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => {}, + } + } + + fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { + let body_owner = cx.tcx.hir().body_owner(body.id()); + let body_span = cx.tcx.hir().span(body_owner); + if let Some(span) = self.const_span && span.contains(body_span) { + return; + } + self.const_span = None; + } + + fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if Some(expr.span) == self.expr_span { + self.expr_span = None; + } + } +} diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 979e0a667..945a09a64 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -8,6 +8,10 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_lint::LateContext; +use rustc_middle::mir::FakeReadCause; +use rustc_middle::ty::BorrowKind; +use rustc_trait_selection::infer::TyCtxtInferExt; +use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use super::ASSIGN_OP_PATTERN; @@ -29,6 +33,16 @@ pub(super) fn check<'tcx>( .map_or(true, |t| t.path.res.def_id() != trait_id); if implements_trait(cx, ty, trait_id, &[rty.into()]); then { + // Primitive types execute assign-ops right-to-left. Every other type is left-to-right. + if !(ty.is_primitive() && rty.is_primitive()) { + // TODO: This will have false negatives as it doesn't check if the borrows are + // actually live at the end of their respective expressions. + let mut_borrows = mut_borrows_in_expr(cx, assignee); + let imm_borrows = imm_borrows_in_expr(cx, rhs); + if mut_borrows.iter().any(|id| imm_borrows.contains(id)) { + return; + } + } span_lint_and_then( cx, ASSIGN_OP_PATTERN, @@ -99,3 +113,69 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> { walk_expr(self, expr); } } + +fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet { + struct S(hir::HirIdSet); + impl Delegate<'_> for S { + fn borrow(&mut self, place: &PlaceWithHirId<'_>, _: hir::HirId, kind: BorrowKind) { + if matches!(kind, BorrowKind::ImmBorrow | BorrowKind::UniqueImmBorrow) { + self.0.insert(match place.place.base { + PlaceBase::Local(id) => id, + PlaceBase::Upvar(id) => id.var_path.hir_id, + _ => return, + }); + } + } + + fn consume(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {} + fn mutate(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {} + fn fake_read(&mut self, _: &PlaceWithHirId<'_>, _: FakeReadCause, _: hir::HirId) {} + fn copy(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {} + } + + let mut s = S(hir::HirIdSet::default()); + cx.tcx.infer_ctxt().enter(|infcx| { + let mut v = ExprUseVisitor::new( + &mut s, + &infcx, + cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + cx.param_env, + cx.typeck_results(), + ); + v.consume_expr(e); + }); + s.0 +} + +fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet { + struct S(hir::HirIdSet); + impl Delegate<'_> for S { + fn borrow(&mut self, place: &PlaceWithHirId<'_>, _: hir::HirId, kind: BorrowKind) { + if matches!(kind, BorrowKind::MutBorrow) { + self.0.insert(match place.place.base { + PlaceBase::Local(id) => id, + PlaceBase::Upvar(id) => id.var_path.hir_id, + _ => return, + }); + } + } + + fn consume(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {} + fn mutate(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {} + fn fake_read(&mut self, _: &PlaceWithHirId<'_>, _: FakeReadCause, _: hir::HirId) {} + fn copy(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {} + } + + let mut s = S(hir::HirIdSet::default()); + cx.tcx.infer_ctxt().enter(|infcx| { + let mut v = ExprUseVisitor::new( + &mut s, + &infcx, + cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + cx.param_env, + cx.typeck_results(), + ); + v.consume_expr(e); + }); + s.0 +} diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 35fe405bc..bb6d99406 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -1,7 +1,3 @@ -use rustc_hir::{Body, Expr, ExprKind, UnOp}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; - mod absurd_extreme_comparisons; mod assign_op_pattern; mod bit_mask; @@ -25,6 +21,12 @@ mod ptr_eq; mod self_assignment; mod verbose_bit_mask; +pub(crate) mod arithmetic; + +use rustc_hir::{Body, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + declare_clippy_lint! { /// ### What it does /// Checks for comparisons where one side of the relation is @@ -57,6 +59,42 @@ declare_clippy_lint! { "a comparison with a maximum or minimum value that is always true or false" } +declare_clippy_lint! { + /// ### What it does + /// Checks for any kind of arithmetic operation of any type. + /// + /// Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing according to the [Rust + /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), + /// or can panic (`/`, `%`). Known safe built-in types like `Wrapping` or `Saturing` are filtered + /// away. + /// + /// ### Why is this bad? + /// Integer overflow will trigger a panic in debug builds or will wrap in + /// release mode. Division by zero will cause a panic in either mode. In some applications one + /// wants explicitly checked, wrapping or saturating arithmetic. + /// + /// #### Example + /// ```rust + /// # let a = 0; + /// a + 1; + /// ``` + /// + /// Third-party types also tend to overflow. + /// + /// #### Example + /// ```ignore,rust + /// use rust_decimal::Decimal; + /// let _n = Decimal::MAX + Decimal::MAX; + /// ``` + /// + /// ### Allowed types + /// Custom allowed types can be specified through the "arithmetic-allowed" filter. + #[clippy::version = "1.64.0"] + pub ARITHMETIC, + restriction, + "any arithmetic expression that could overflow or panic" +} + declare_clippy_lint! { /// ### What it does /// Checks for integer arithmetic operations which could overflow or panic. @@ -747,6 +785,7 @@ pub struct Operators { } impl_lint_pass!(Operators => [ ABSURD_EXTREME_COMPARISONS, + ARITHMETIC, INTEGER_ARITHMETIC, FLOAT_ARITHMETIC, ASSIGN_OP_PATTERN, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index f0155ed60..fd0a53839 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -123,8 +123,8 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: if_chain! { if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr); if !is_else_clause(cx.tcx, expr); - if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind; - if let PatKind::Binding(annot, bind_id, ident, _) = fields[0].kind; + if let PatKind::TupleStruct(ref path1, [field], None) = let_pat.kind; + if let PatKind::Binding(annot, bind_id, ident, _) = field.kind; let caller_ty = cx.typeck_results().expr_ty(let_expr); let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else); if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 547d4da81..056637c20 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -396,7 +396,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: // `.iter()` and `.len()` called on same `Path` if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind; if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); + if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments); then { span_lint(cx, RANGE_ZIP_WITH_LEN, diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 20184d54b..73f8e083b 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -55,11 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { if matches!(cx.tcx.def_kind(id.def_id), DefKind::Impl) && let item = cx.tcx.hir().item(id) && let ItemKind::Impl(Impl { - items, - of_trait, - self_ty, - .. - }) = &item.kind + items, + of_trait, + self_ty, + .. + }) = &item.kind && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { if !map.contains_key(res) { diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 56f2a7bae..ffd63cc68 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{def::Res, HirId, Path, PathSegment}; -use rustc_lint::{LateContext, LateLintPass, Lint}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{sym, symbol::kw, Symbol}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{sym, symbol::kw, Span}; declare_clippy_lint! { /// ### What it does @@ -63,7 +63,7 @@ declare_clippy_lint! { /// ### Why is this bad? /// /// Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are - /// imported from alloc to ensure disabling `alloc` does not cause the crate to fail to compile. This lint + /// imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint /// is also useful for crates migrating to become `no_std` compatible. /// /// ### Example @@ -81,39 +81,55 @@ declare_clippy_lint! { "type is imported from alloc when available in core" } -declare_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]); +#[derive(Default)] +pub struct StdReexports { + // Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen + // twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro + // when the path could be also be used to access the module. + prev_span: Span, +} +impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]); impl<'tcx> LateLintPass<'tcx> for StdReexports { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { - // std_instead_of_core - check_path(cx, path, sym::std, sym::core, STD_INSTEAD_OF_CORE); - // std_instead_of_alloc - check_path(cx, path, sym::std, sym::alloc, STD_INSTEAD_OF_ALLOC); - // alloc_instead_of_core - check_path(cx, path, sym::alloc, sym::core, ALLOC_INSTEAD_OF_CORE); - } -} - -fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_crate: Symbol, lint: &'static Lint) { - if_chain! { - // check if path resolves to the suggested crate. - if let Res::Def(_, def_id) = path.res; - if suggested_crate == cx.tcx.crate_name(def_id.krate); - - // check if the first segment of the path is the crate we want to identify - if let Some(path_root_segment) = get_first_segment(path); - - // check if the path matches the crate we want to suggest the other path for. - if krate == path_root_segment.ident.name; - then { - span_lint_and_help( - cx, - lint, - path.span, - &format!("used import from `{}` instead of `{}`", krate, suggested_crate), - None, - &format!("consider importing the item from `{}`", suggested_crate), - ); + if let Res::Def(_, def_id) = path.res + && let Some(first_segment) = get_first_segment(path) + { + let (lint, msg, help) = match first_segment.ident.name { + sym::std => match cx.tcx.crate_name(def_id.krate) { + sym::core => ( + STD_INSTEAD_OF_CORE, + "used import from `std` instead of `core`", + "consider importing the item from `core`", + ), + sym::alloc => ( + STD_INSTEAD_OF_ALLOC, + "used import from `std` instead of `alloc`", + "consider importing the item from `alloc`", + ), + _ => { + self.prev_span = path.span; + return; + }, + }, + sym::alloc => { + if cx.tcx.crate_name(def_id.krate) == sym::core { + ( + ALLOC_INSTEAD_OF_CORE, + "used import from `alloc` instead of `core`", + "consider importing the item from `core`", + ) + } else { + self.prev_span = path.span; + return; + } + }, + _ => return, + }; + if path.span != self.prev_span { + span_lint_and_help(cx, lint, path.span, msg, None, help); + self.prev_span = path.span; + } } } } @@ -123,12 +139,10 @@ fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_cr /// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`] /// is returned. fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { - let segment = path.segments.first()?; - - // A global path will have PathRoot as the first segment. In this case, return the segment after. - if segment.ident.name == kw::PathRoot { - path.segments.get(1) - } else { - Some(segment) + match path.segments { + // A global path will have PathRoot as the first segment. In this case, return the segment after. + [x, y, ..] if x.ident.name == kw::PathRoot => Some(y), + [x, ..] => Some(x), + _ => None, } } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index eb704a074..22eb06b36 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -394,7 +394,7 @@ impl<'tcx> LateLintPass<'tcx> for StrToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind; - if path.ident.name == sym!(to_string); + if path.ident.name == sym::to_string; let ty = cx.typeck_results().expr_ty(self_arg); if let ty::Ref(_, ty, ..) = ty.kind(); if *ty.kind() == ty::Str; @@ -444,7 +444,7 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind; - if path.ident.name == sym!(to_string); + if path.ident.name == sym::to_string; let ty = cx.typeck_results().expr_ty(self_arg); if is_type_diagnostic_item(cx, ty, sym::String); then { diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index fd9d5b52e..51c65d898 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -3,7 +3,7 @@ use clippy_utils::visitors::is_local_used; use if_chain::if_chain; use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { /// ### What it does @@ -35,7 +35,19 @@ declare_clippy_lint! { "methods that contain a `self` argument but don't use it" } -declare_lint_pass!(UnusedSelf => [UNUSED_SELF]); +pub struct UnusedSelf { + avoid_breaking_exported_api: bool, +} + +impl_lint_pass!(UnusedSelf => [UNUSED_SELF]); + +impl UnusedSelf { + pub fn new(avoid_breaking_exported_api: bool) -> Self { + Self { + avoid_breaking_exported_api, + } + } +} impl<'tcx> LateLintPass<'tcx> for UnusedSelf { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'_>) { @@ -49,6 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind; if assoc_item.fn_has_self_parameter; if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; + if !cx.access_levels.is_exported(impl_item.def_id) || !self.avoid_breaking_exported_api; let body = cx.tcx.hir().body(*body_id); if let [self_param, ..] = body.params; if !is_local_used(cx, body, self_param.pat.hir_id); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 38e5c5e5b..6e033b3be 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -191,7 +191,11 @@ macro_rules! define_Conf { } define_Conf! { - /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX. + /// Lint: Arithmetic. + /// + /// Suppress checking of the passed type names. + (arithmetic_allowed: rustc_data_structures::fx::FxHashSet = <_>::default()), + /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX. /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 6d4a48b53..351a3f4ae 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -619,32 +619,24 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) - }, mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() { ty::Array(sub_type, len) => match sub_type.kind() { - ty::Float(FloatTy::F32) => match len.to_valtree().try_to_machine_usize(tcx) { + ty::Float(FloatTy::F32) => match len.kind().try_to_machine_usize(tcx) { Some(len) => alloc .inner() .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap())) .to_owned() - .chunks(4) - .map(|chunk| { - Some(Constant::F32(f32::from_le_bytes( - chunk.try_into().expect("this shouldn't happen"), - ))) - }) + .array_chunks::<4>() + .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk)))) .collect::>>() .map(Constant::Vec), _ => None, }, - ty::Float(FloatTy::F64) => match len.to_valtree().try_to_machine_usize(tcx) { + ty::Float(FloatTy::F64) => match len.kind().try_to_machine_usize(tcx) { Some(len) => alloc .inner() .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap())) .to_owned() - .chunks(8) - .map(|chunk| { - Some(Constant::F64(f64::from_le_bytes( - chunk.try_into().expect("this shouldn't happen"), - ))) - }) + .array_chunks::<8>() + .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk)))) .collect::>>() .map(Constant::Vec), _ => None, diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 7f55db3b3..ad95369b9 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -155,13 +155,7 @@ where }); } -pub fn span_lint_hir( - cx: &LateContext<'_>, - lint: &'static Lint, - hir_id: HirId, - sp: Span, - msg: &str, -) { +pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) { cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| { let mut diag = diag.build(msg); docs_link(&mut diag, lint); diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 77c974582..eaf260ddf 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -127,9 +127,6 @@ impl HirEqInterExpr<'_, '_, '_> { /// Checks whether two blocks are the same. fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { - if self.cannot_be_compared_block(left) || self.cannot_be_compared_block(right) { - return false; - } match (left.stmts, left.expr, right.stmts, right.expr) { ([], None, [], None) => { // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro @@ -180,36 +177,13 @@ impl HirEqInterExpr<'_, '_, '_> { } } - fn cannot_be_compared_block(&mut self, block: &Block<'_>) -> bool { - if block.stmts.last().map_or(false, |stmt| { - matches!( - stmt.kind, - StmtKind::Semi(semi_expr) if self.should_ignore(semi_expr) - ) - }) { - return true; - } - - if let Some(block_expr) = block.expr - && self.should_ignore(block_expr) - { - return true - } - - false - } - fn should_ignore(&mut self, expr: &Expr<'_>) -> bool { - if macro_backtrace(expr.span).last().map_or(false, |macro_call| { + macro_backtrace(expr.span).last().map_or(false, |macro_call| { matches!( &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::todo_macro | sym::unimplemented_macro) ) - }) { - return true; - } - - false + }) } pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool { @@ -327,7 +301,8 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re), _ => false, }; - is_eq || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right)) + (is_eq && (!self.should_ignore(left) || !self.should_ignore(right))) + || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right)) } fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2fdda9fac..9ffbc73dc 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1,7 +1,7 @@ +#![feature(array_chunks)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(let_else)] -#![cfg_attr(bootstrap, feature(let_chains))] #![feature(lint_reasons)] #![feature(once_cell)] #![feature(rustc_private)] @@ -2141,7 +2141,7 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { static TEST_ITEM_NAMES_CACHE: OnceLock>>> = OnceLock::new(); -fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { +fn with_test_item_names(tcx: TyCtxt<'_>, 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(); let value = map.entry(module); diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 3615d0715..92ac1a2be 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -433,7 +433,7 @@ fn rustfix_coverage_known_exceptions_accuracy() { let rs_path = Path::new("tests/ui").join(filename); assert!( rs_path.exists(), - "`{}` does not exists", + "`{}` does not exist", rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display() ); let fixed_path = rs_path.with_extension("fixed"); @@ -445,6 +445,45 @@ fn rustfix_coverage_known_exceptions_accuracy() { } } +#[test] +fn ui_cargo_toml_metadata() { + let ui_cargo_path = Path::new("tests/ui-cargo"); + let cargo_common_metadata_path = ui_cargo_path.join("cargo_common_metadata"); + let publish_exceptions = + ["fail_publish", "fail_publish_true", "pass_publish_empty"].map(|path| cargo_common_metadata_path.join(path)); + + for entry in walkdir::WalkDir::new(ui_cargo_path) { + let entry = entry.unwrap(); + let path = entry.path(); + if path.file_name() != Some(OsStr::new("Cargo.toml")) { + continue; + } + + let toml = fs::read_to_string(path).unwrap().parse::().unwrap(); + + let package = toml.as_table().unwrap().get("package").unwrap().as_table().unwrap(); + + let name = package.get("name").unwrap().as_str().unwrap().replace('-', "_"); + assert!( + path.parent() + .unwrap() + .components() + .map(|component| component.as_os_str().to_string_lossy().replace('-', "_")) + .any(|s| *s == name) + || path.starts_with(&cargo_common_metadata_path), + "{:?} has incorrect package name", + path + ); + + let publish = package.get("publish").and_then(toml::Value::as_bool).unwrap_or(true); + assert!( + !publish || publish_exceptions.contains(&path.parent().unwrap().to_path_buf()), + "{:?} lacks `publish = false`", + path + ); + } +} + /// Restores an env var on drop #[must_use] struct VarGuard { diff --git a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml index ae0a60329..bc8e428f8 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cargo_common_metadata" +name = "cargo_common_metadata_fail" version = "0.1.0" publish = false diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr index 5e9aa8dc3..86953142b 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr @@ -1,16 +1,16 @@ -error: package `cargo_common_metadata` is missing `package.description` metadata +error: package `cargo_common_metadata_fail` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` -error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata +error: package `cargo_common_metadata_fail` is missing `either package.license or package.license_file` metadata -error: package `cargo_common_metadata` is missing `package.repository` metadata +error: package `cargo_common_metadata_fail` is missing `package.repository` metadata -error: package `cargo_common_metadata` is missing `package.readme` metadata +error: package `cargo_common_metadata_fail` is missing `package.readme` metadata -error: package `cargo_common_metadata` is missing `package.keywords` metadata +error: package `cargo_common_metadata_fail` is missing `package.keywords` metadata -error: package `cargo_common_metadata` is missing `package.categories` metadata +error: package `cargo_common_metadata_fail` is missing `package.categories` metadata error: aborting due to 6 previous errors diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.toml index 759569635..5005b83f5 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cargo_common_metadata" +name = "cargo_common_metadata_fail_publish" version = "0.1.0" publish = ["some-registry-name"] diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr index 5e9aa8dc3..ac1b5e8e9 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr @@ -1,16 +1,16 @@ -error: package `cargo_common_metadata` is missing `package.description` metadata +error: package `cargo_common_metadata_fail_publish` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` -error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata +error: package `cargo_common_metadata_fail_publish` is missing `either package.license or package.license_file` metadata -error: package `cargo_common_metadata` is missing `package.repository` metadata +error: package `cargo_common_metadata_fail_publish` is missing `package.repository` metadata -error: package `cargo_common_metadata` is missing `package.readme` metadata +error: package `cargo_common_metadata_fail_publish` is missing `package.readme` metadata -error: package `cargo_common_metadata` is missing `package.keywords` metadata +error: package `cargo_common_metadata_fail_publish` is missing `package.keywords` metadata -error: package `cargo_common_metadata` is missing `package.categories` metadata +error: package `cargo_common_metadata_fail_publish` is missing `package.categories` metadata error: aborting due to 6 previous errors diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.toml index 7e5b88383..51858eecd 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cargo_common_metadata" +name = "cargo_common_metadata_fail_publish_true" version = "0.1.0" publish = true diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr index 5e9aa8dc3..be32c0dc4 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr @@ -1,16 +1,16 @@ -error: package `cargo_common_metadata` is missing `package.description` metadata +error: package `cargo_common_metadata_fail_publish_true` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` -error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata +error: package `cargo_common_metadata_fail_publish_true` is missing `either package.license or package.license_file` metadata -error: package `cargo_common_metadata` is missing `package.repository` metadata +error: package `cargo_common_metadata_fail_publish_true` is missing `package.repository` metadata -error: package `cargo_common_metadata` is missing `package.readme` metadata +error: package `cargo_common_metadata_fail_publish_true` is missing `package.readme` metadata -error: package `cargo_common_metadata` is missing `package.keywords` metadata +error: package `cargo_common_metadata_fail_publish_true` is missing `package.keywords` metadata -error: package `cargo_common_metadata` is missing `package.categories` metadata +error: package `cargo_common_metadata_fail_publish_true` is missing `package.categories` metadata error: aborting due to 6 previous errors diff --git a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml index cb4774d43..9f6e51fb4 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cargo_common_metadata" +name = "cargo_common_metadata_pass" version = "0.1.0" publish = false description = "A test package for the cargo_common_metadata lint" diff --git a/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/Cargo.toml index 0a879c99b..828efee3a 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/pass_publish_empty/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cargo_common_metadata" +name = "cargo_common_metadata_pass_publish_empty" version = "0.1.0" publish = [] diff --git a/tests/ui-cargo/cargo_common_metadata/pass_publish_false/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass_publish_false/Cargo.toml index ae0a60329..45a5bf7c5 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass_publish_false/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/pass_publish_false/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cargo_common_metadata" +name = "cargo_common_metadata_pass_publish_false" version = "0.1.0" publish = false diff --git a/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.toml b/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.toml index 73ec29c58..946d1b366 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.toml +++ b/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.toml @@ -2,6 +2,7 @@ name = "fail-both-diff" version = "0.1.0" rust-version = "1.56" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.toml b/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.toml index 2d6d547e4..46b92a105 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.toml +++ b/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.toml @@ -2,6 +2,7 @@ name = "fail-both-same" version = "0.1.0" rust-version = "1.57.0" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.toml b/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.toml index 36a53bd82..189cc9f68 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.toml +++ b/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.toml @@ -2,6 +2,7 @@ name = "fail-cargo" version = "0.1.0" rust-version = "1.56.1" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.toml b/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.toml index 9f644a1a3..bdb7f261d 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.toml +++ b/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "fail-clippy" version = "0.1.0" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.toml b/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.toml index 5380e993b..84448ea41 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.toml +++ b/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.toml @@ -2,6 +2,7 @@ name = "fail-file-attr" version = "0.1.0" rust-version = "1.13" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/cargo_rust_version/pass_both_same/Cargo.toml b/tests/ui-cargo/cargo_rust_version/pass_both_same/Cargo.toml index 1f9bd8f9a..809c0e748 100644 --- a/tests/ui-cargo/cargo_rust_version/pass_both_same/Cargo.toml +++ b/tests/ui-cargo/cargo_rust_version/pass_both_same/Cargo.toml @@ -1,7 +1,8 @@ [package] -name = "fail-both-same" +name = "pass-both-same" version = "0.1.0" rust-version = "1.13.0" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/cargo_rust_version/pass_cargo/Cargo.toml b/tests/ui-cargo/cargo_rust_version/pass_cargo/Cargo.toml index 77538027c..32d404f84 100644 --- a/tests/ui-cargo/cargo_rust_version/pass_cargo/Cargo.toml +++ b/tests/ui-cargo/cargo_rust_version/pass_cargo/Cargo.toml @@ -1,7 +1,8 @@ [package] -name = "fail-cargo" +name = "pass-cargo" version = "0.1.0" rust-version = "1.13.0" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/cargo_rust_version/pass_clippy/Cargo.toml b/tests/ui-cargo/cargo_rust_version/pass_clippy/Cargo.toml index 9f644a1a3..cc937d6e6 100644 --- a/tests/ui-cargo/cargo_rust_version/pass_clippy/Cargo.toml +++ b/tests/ui-cargo/cargo_rust_version/pass_clippy/Cargo.toml @@ -1,6 +1,7 @@ [package] -name = "fail-clippy" +name = "pass-clippy" version = "0.1.0" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/cargo_rust_version/pass_file_attr/Cargo.toml b/tests/ui-cargo/cargo_rust_version/pass_file_attr/Cargo.toml index f0387cd90..8ef689880 100644 --- a/tests/ui-cargo/cargo_rust_version/pass_file_attr/Cargo.toml +++ b/tests/ui-cargo/cargo_rust_version/pass_file_attr/Cargo.toml @@ -1,7 +1,8 @@ [package] -name = "fail-file-attr" +name = "pass-file-attr" version = "0.1.0" rust-version = "1.59" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.toml b/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.toml index a19d5b33f..e9f94594f 100644 --- a/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.toml +++ b/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.toml @@ -2,6 +2,7 @@ name = "warn-both-diff" version = "0.1.0" rust-version = "1.56.0" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/module_style/fail_mod/Cargo.toml b/tests/ui-cargo/module_style/fail_mod/Cargo.toml index 27b61c09f..b3d36a9fb 100644 --- a/tests/ui-cargo/module_style/fail_mod/Cargo.toml +++ b/tests/ui-cargo/module_style/fail_mod/Cargo.toml @@ -1,7 +1,8 @@ [package] -name = "fail" +name = "fail-mod" version = "0.1.0" edition = "2018" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml b/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml index 27b61c09f..3610d13c1 100644 --- a/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml +++ b/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml @@ -1,7 +1,8 @@ [package] -name = "fail" +name = "fail-no-mod" version = "0.1.0" edition = "2018" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/module_style/pass_mod/Cargo.toml b/tests/ui-cargo/module_style/pass_mod/Cargo.toml index 27b61c09f..1c2991695 100644 --- a/tests/ui-cargo/module_style/pass_mod/Cargo.toml +++ b/tests/ui-cargo/module_style/pass_mod/Cargo.toml @@ -1,7 +1,8 @@ [package] -name = "fail" +name = "pass-mod" version = "0.1.0" edition = "2018" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml b/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml index 3c0896dd2..4180aaf51 100644 --- a/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml +++ b/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml @@ -1,7 +1,8 @@ [package] -name = "pass" +name = "pass-no-mod" version = "0.1.0" edition = "2018" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/multiple_config_files/no_warn/Cargo.toml b/tests/ui-cargo/multiple_config_files/no_warn/Cargo.toml index 79c973cbf..7eb56cc4e 100644 --- a/tests/ui-cargo/multiple_config_files/no_warn/Cargo.toml +++ b/tests/ui-cargo/multiple_config_files/no_warn/Cargo.toml @@ -2,6 +2,7 @@ name = "no_warn" version = "0.1.0" edition = "2021" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/multiple_config_files/warn/Cargo.toml b/tests/ui-cargo/multiple_config_files/warn/Cargo.toml index 3d5c70757..b4847d070 100644 --- a/tests/ui-cargo/multiple_config_files/warn/Cargo.toml +++ b/tests/ui-cargo/multiple_config_files/warn/Cargo.toml @@ -2,6 +2,7 @@ name = "warn" version = "0.1.0" edition = "2021" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/ui-cargo/multiple_config_files/warn/src/main.rs b/tests/ui-cargo/multiple_config_files/warn/src/main.rs index 2d0b4a794..e7a11a969 100644 --- a/tests/ui-cargo/multiple_config_files/warn/src/main.rs +++ b/tests/ui-cargo/multiple_config_files/warn/src/main.rs @@ -1,5 +1,3 @@ -// ignore-windows - fn main() { println!("Hello, world!"); } diff --git a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml index b4b49bb36..6c46571c5 100644 --- a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml +++ b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cargo_common_metadata" +name = "multiple_crate_versions" version = "0.1.0" publish = false diff --git a/tests/ui-internal/check_clippy_version_attribute.stderr b/tests/ui-internal/check_clippy_version_attribute.stderr index 67e1a07b7..533107588 100644 --- a/tests/ui-internal/check_clippy_version_attribute.stderr +++ b/tests/ui-internal/check_clippy_version_attribute.stderr @@ -17,7 +17,7 @@ LL | #![deny(clippy::internal)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` = help: please use a valid sematic version, see `doc/adding_lints.md` - = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this item has an invalid `clippy::version` attribute --> $DIR/check_clippy_version_attribute.rs:48:1 @@ -32,7 +32,7 @@ LL | | } | |_^ | = help: please use a valid sematic version, see `doc/adding_lints.md` - = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value --> $DIR/check_clippy_version_attribute.rs:59:1 @@ -48,7 +48,7 @@ LL | | } | = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` = help: please use a `clippy::version` attribute, see `doc/adding_lints.md` - = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value --> $DIR/check_clippy_version_attribute.rs:67:1 @@ -62,7 +62,7 @@ LL | | } | |_^ | = help: please use a `clippy::version` attribute, see `doc/adding_lints.md` - = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui-internal/default_lint.stderr b/tests/ui-internal/default_lint.stderr index af6735f4e..8961bd462 100644 --- a/tests/ui-internal/default_lint.stderr +++ b/tests/ui-internal/default_lint.stderr @@ -15,7 +15,7 @@ note: the lint level is defined here LL | #![deny(clippy::internal)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` - = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/tests/ui-internal/if_chain_style.stderr b/tests/ui-internal/if_chain_style.stderr index d0f100f00..24106510e 100644 --- a/tests/ui-internal/if_chain_style.stderr +++ b/tests/ui-internal/if_chain_style.stderr @@ -56,7 +56,7 @@ LL | | } LL | | } | |_____^ | - = note: this error originates in the macro `__if_chain` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__if_chain` which comes from the expansion of the macro `if_chain` (in Nightly builds, run with -Z macro-backtrace for more info) error: `let` expression should be above the `if_chain!` --> $DIR/if_chain_style.rs:40:9 diff --git a/tests/ui-toml/arithmetic_allowed/arithmetic_allowed.rs b/tests/ui-toml/arithmetic_allowed/arithmetic_allowed.rs new file mode 100644 index 000000000..195fabdbf --- /dev/null +++ b/tests/ui-toml/arithmetic_allowed/arithmetic_allowed.rs @@ -0,0 +1,24 @@ +#![warn(clippy::arithmetic)] + +use core::ops::Add; + +#[derive(Clone, Copy)] +struct Point { + x: i32, + y: i32, +} + +impl Add for Point { + type Output = Self; + + fn add(self, other: Self) -> Self { + todo!() + } +} + +fn main() { + let _ = Point { x: 1, y: 0 } + Point { x: 2, y: 3 }; + + let point: Point = Point { x: 1, y: 0 }; + let _ = point + point; +} diff --git a/tests/ui-toml/arithmetic_allowed/clippy.toml b/tests/ui-toml/arithmetic_allowed/clippy.toml new file mode 100644 index 000000000..cc40570b1 --- /dev/null +++ b/tests/ui-toml/arithmetic_allowed/clippy.toml @@ -0,0 +1 @@ +arithmetic-allowed = ["Point"] 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 1d87fd91a..fe5139c47 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -3,6 +3,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie allow-expect-in-tests allow-unwrap-in-tests allowed-scripts + arithmetic-allowed array-size-threshold avoid-breaking-exported-api await-holding-invalid-types diff --git a/tests/ui/arithmetic.fixed b/tests/ui/arithmetic.fixed new file mode 100644 index 000000000..a2a1c4394 --- /dev/null +++ b/tests/ui/arithmetic.fixed @@ -0,0 +1,27 @@ +// run-rustfix + +#![allow(clippy::unnecessary_owned_empty_strings)] +#![feature(saturating_int_impl)] +#![warn(clippy::arithmetic)] + +use core::num::{Saturating, Wrapping}; + +pub fn hard_coded_allowed() { + let _ = Saturating(0u32) + Saturating(0u32); + let _ = String::new() + ""; + let _ = Wrapping(0u32) + Wrapping(0u32); + + let saturating: Saturating = Saturating(0u32); + let string: String = String::new(); + let wrapping: Wrapping = Wrapping(0u32); + + let inferred_saturating = saturating + saturating; + let inferred_string = string + ""; + let inferred_wrapping = wrapping + wrapping; + + let _ = inferred_saturating + inferred_saturating; + let _ = inferred_string + ""; + let _ = inferred_wrapping + inferred_wrapping; +} + +fn main() {} diff --git a/tests/ui/arithmetic.rs b/tests/ui/arithmetic.rs new file mode 100644 index 000000000..a2a1c4394 --- /dev/null +++ b/tests/ui/arithmetic.rs @@ -0,0 +1,27 @@ +// run-rustfix + +#![allow(clippy::unnecessary_owned_empty_strings)] +#![feature(saturating_int_impl)] +#![warn(clippy::arithmetic)] + +use core::num::{Saturating, Wrapping}; + +pub fn hard_coded_allowed() { + let _ = Saturating(0u32) + Saturating(0u32); + let _ = String::new() + ""; + let _ = Wrapping(0u32) + Wrapping(0u32); + + let saturating: Saturating = Saturating(0u32); + let string: String = String::new(); + let wrapping: Wrapping = Wrapping(0u32); + + let inferred_saturating = saturating + saturating; + let inferred_string = string + ""; + let inferred_wrapping = wrapping + wrapping; + + let _ = inferred_saturating + inferred_saturating; + let _ = inferred_string + ""; + let _ = inferred_wrapping + inferred_wrapping; +} + +fn main() {} diff --git a/tests/ui/assertions_on_result_states.fixed b/tests/ui/assertions_on_result_states.fixed new file mode 100644 index 000000000..7bde72e4b --- /dev/null +++ b/tests/ui/assertions_on_result_states.fixed @@ -0,0 +1,69 @@ +// run-rustfix +#![warn(clippy::assertions_on_result_states)] + +use std::result::Result; + +struct Foo; + +#[derive(Debug)] +struct DebugFoo; + +#[derive(Copy, Clone, Debug)] +struct CopyFoo; + +macro_rules! get_ok_macro { + () => { + Ok::<_, DebugFoo>(Foo) + }; +} + +fn main() { + // test ok + let r: Result = Ok(Foo); + debug_assert!(r.is_ok()); + r.unwrap(); + + // test ok with non-debug error type + let r: Result = Ok(Foo); + assert!(r.is_ok()); + + // test temporary ok + fn get_ok() -> Result { + Ok(Foo) + } + get_ok().unwrap(); + + // test macro ok + get_ok_macro!().unwrap(); + + // test ok that shouldn't be moved + let r: Result = Ok(CopyFoo); + fn test_ref_unmoveable_ok(r: &Result) { + assert!(r.is_ok()); + } + test_ref_unmoveable_ok(&r); + assert!(r.is_ok()); + r.unwrap(); + + // test ok that is copied + let r: Result = Ok(CopyFoo); + r.unwrap(); + r.unwrap(); + + // test reference to ok + let r: Result = Ok(CopyFoo); + fn test_ref_copy_ok(r: &Result) { + r.unwrap(); + } + test_ref_copy_ok(&r); + r.unwrap(); + + // test err + let r: Result = Err(Foo); + debug_assert!(r.is_err()); + r.unwrap_err(); + + // test err with non-debug value type + let r: Result = Err(Foo); + assert!(r.is_err()); +} diff --git a/tests/ui/assertions_on_result_states.rs b/tests/ui/assertions_on_result_states.rs new file mode 100644 index 000000000..4c5af81ef --- /dev/null +++ b/tests/ui/assertions_on_result_states.rs @@ -0,0 +1,69 @@ +// run-rustfix +#![warn(clippy::assertions_on_result_states)] + +use std::result::Result; + +struct Foo; + +#[derive(Debug)] +struct DebugFoo; + +#[derive(Copy, Clone, Debug)] +struct CopyFoo; + +macro_rules! get_ok_macro { + () => { + Ok::<_, DebugFoo>(Foo) + }; +} + +fn main() { + // test ok + let r: Result = Ok(Foo); + debug_assert!(r.is_ok()); + assert!(r.is_ok()); + + // test ok with non-debug error type + let r: Result = Ok(Foo); + assert!(r.is_ok()); + + // test temporary ok + fn get_ok() -> Result { + Ok(Foo) + } + assert!(get_ok().is_ok()); + + // test macro ok + assert!(get_ok_macro!().is_ok()); + + // test ok that shouldn't be moved + let r: Result = Ok(CopyFoo); + fn test_ref_unmoveable_ok(r: &Result) { + assert!(r.is_ok()); + } + test_ref_unmoveable_ok(&r); + assert!(r.is_ok()); + r.unwrap(); + + // test ok that is copied + let r: Result = Ok(CopyFoo); + assert!(r.is_ok()); + r.unwrap(); + + // test reference to ok + let r: Result = Ok(CopyFoo); + fn test_ref_copy_ok(r: &Result) { + assert!(r.is_ok()); + } + test_ref_copy_ok(&r); + r.unwrap(); + + // test err + let r: Result = Err(Foo); + debug_assert!(r.is_err()); + assert!(r.is_err()); + + // test err with non-debug value type + let r: Result = Err(Foo); + assert!(r.is_err()); +} diff --git a/tests/ui/assertions_on_result_states.stderr b/tests/ui/assertions_on_result_states.stderr new file mode 100644 index 000000000..13c2dd877 --- /dev/null +++ b/tests/ui/assertions_on_result_states.stderr @@ -0,0 +1,40 @@ +error: called `assert!` with `Result::is_ok` + --> $DIR/assertions_on_result_states.rs:24:5 + | +LL | assert!(r.is_ok()); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()` + | + = note: `-D clippy::assertions-on-result-states` implied by `-D warnings` + +error: called `assert!` with `Result::is_ok` + --> $DIR/assertions_on_result_states.rs:34:5 + | +LL | assert!(get_ok().is_ok()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get_ok().unwrap()` + +error: called `assert!` with `Result::is_ok` + --> $DIR/assertions_on_result_states.rs:37:5 + | +LL | assert!(get_ok_macro!().is_ok()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get_ok_macro!().unwrap()` + +error: called `assert!` with `Result::is_ok` + --> $DIR/assertions_on_result_states.rs:50:5 + | +LL | assert!(r.is_ok()); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()` + +error: called `assert!` with `Result::is_ok` + --> $DIR/assertions_on_result_states.rs:56:9 + | +LL | assert!(r.is_ok()); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()` + +error: called `assert!` with `Result::is_err` + --> $DIR/assertions_on_result_states.rs:64:5 + | +LL | assert!(r.is_err()); + | ^^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap_err()` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/assign_ops.fixed b/tests/ui/assign_ops.fixed index 52b1b3afe..da034b51c 100644 --- a/tests/ui/assign_ops.fixed +++ b/tests/ui/assign_ops.fixed @@ -1,5 +1,7 @@ // run-rustfix +use core::num::Wrapping; + #[allow(dead_code, unused_assignments)] #[warn(clippy::assign_op_pattern)] fn main() { @@ -18,4 +20,13 @@ fn main() { a = 6 << a; let mut s = String::new(); s += "bla"; + + // Issue #9180 + let mut a = Wrapping(0u32); + a += Wrapping(1u32); + let mut v = vec![0u32, 1u32]; + v[0] += v[1]; + let mut v = vec![Wrapping(0u32), Wrapping(1u32)]; + v[0] = v[0] + v[1]; + let _ = || v[0] = v[0] + v[1]; } diff --git a/tests/ui/assign_ops.rs b/tests/ui/assign_ops.rs index 527a46b2c..337bb02c8 100644 --- a/tests/ui/assign_ops.rs +++ b/tests/ui/assign_ops.rs @@ -1,5 +1,7 @@ // run-rustfix +use core::num::Wrapping; + #[allow(dead_code, unused_assignments)] #[warn(clippy::assign_op_pattern)] fn main() { @@ -18,4 +20,13 @@ fn main() { a = 6 << a; let mut s = String::new(); s = s + "bla"; + + // Issue #9180 + let mut a = Wrapping(0u32); + a = a + Wrapping(1u32); + let mut v = vec![0u32, 1u32]; + v[0] = v[0] + v[1]; + let mut v = vec![Wrapping(0u32), Wrapping(1u32)]; + v[0] = v[0] + v[1]; + let _ = || v[0] = v[0] + v[1]; } diff --git a/tests/ui/assign_ops.stderr b/tests/ui/assign_ops.stderr index 3486bd8da..63a938ab4 100644 --- a/tests/ui/assign_ops.stderr +++ b/tests/ui/assign_ops.stderr @@ -1,5 +1,5 @@ error: manual implementation of an assign operation - --> $DIR/assign_ops.rs:7:5 + --> $DIR/assign_ops.rs:9:5 | LL | a = a + 1; | ^^^^^^^^^ help: replace it with: `a += 1` @@ -7,52 +7,64 @@ LL | a = a + 1; = note: `-D clippy::assign-op-pattern` implied by `-D warnings` error: manual implementation of an assign operation - --> $DIR/assign_ops.rs:8:5 + --> $DIR/assign_ops.rs:10:5 | LL | a = 1 + a; | ^^^^^^^^^ help: replace it with: `a += 1` error: manual implementation of an assign operation - --> $DIR/assign_ops.rs:9:5 + --> $DIR/assign_ops.rs:11:5 | LL | a = a - 1; | ^^^^^^^^^ help: replace it with: `a -= 1` error: manual implementation of an assign operation - --> $DIR/assign_ops.rs:10:5 + --> $DIR/assign_ops.rs:12:5 | LL | a = a * 99; | ^^^^^^^^^^ help: replace it with: `a *= 99` error: manual implementation of an assign operation - --> $DIR/assign_ops.rs:11:5 + --> $DIR/assign_ops.rs:13:5 | LL | a = 42 * a; | ^^^^^^^^^^ help: replace it with: `a *= 42` error: manual implementation of an assign operation - --> $DIR/assign_ops.rs:12:5 + --> $DIR/assign_ops.rs:14:5 | LL | a = a / 2; | ^^^^^^^^^ help: replace it with: `a /= 2` error: manual implementation of an assign operation - --> $DIR/assign_ops.rs:13:5 + --> $DIR/assign_ops.rs:15:5 | LL | a = a % 5; | ^^^^^^^^^ help: replace it with: `a %= 5` error: manual implementation of an assign operation - --> $DIR/assign_ops.rs:14:5 + --> $DIR/assign_ops.rs:16:5 | LL | a = a & 1; | ^^^^^^^^^ help: replace it with: `a &= 1` error: manual implementation of an assign operation - --> $DIR/assign_ops.rs:20:5 + --> $DIR/assign_ops.rs:22:5 | LL | s = s + "bla"; | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"` -error: aborting due to 9 previous errors +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:26:5 + | +LL | a = a + Wrapping(1u32); + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:28:5 + | +LL | v[0] = v[0] + v[1]; + | ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]` + +error: aborting due to 11 previous errors diff --git a/tests/ui/crashes/ice-9238.rs b/tests/ui/crashes/ice-9238.rs new file mode 100644 index 000000000..ee6abd519 --- /dev/null +++ b/tests/ui/crashes/ice-9238.rs @@ -0,0 +1,12 @@ +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +#![warn(clippy::branches_sharing_code)] + +const fn f() -> usize { + 2 +} +const C: [f64; f()] = [0f64; f()]; + +fn main() { + let _ = if true { C[0] } else { C[1] }; +} diff --git a/tests/ui/crashes/ice-9242.rs b/tests/ui/crashes/ice-9242.rs new file mode 100644 index 000000000..0099e6e2f --- /dev/null +++ b/tests/ui/crashes/ice-9242.rs @@ -0,0 +1,8 @@ +enum E { + X(), + Y, +} + +fn main() { + let _ = if let E::X() = E::X() { 1 } else { 2 }; +} diff --git a/tests/ui/crate_level_checks/entrypoint_recursion.rs b/tests/ui/crate_level_checks/entrypoint_recursion.rs index 7ff5a16ed..1b3bcece6 100644 --- a/tests/ui/crate_level_checks/entrypoint_recursion.rs +++ b/tests/ui/crate_level_checks/entrypoint_recursion.rs @@ -1,5 +1,4 @@ // ignore-macos -// ignore-windows #![feature(rustc_attrs)] diff --git a/tests/ui/crate_level_checks/entrypoint_recursion.stderr b/tests/ui/crate_level_checks/entrypoint_recursion.stderr index f52fc949f..459cf12a1 100644 --- a/tests/ui/crate_level_checks/entrypoint_recursion.stderr +++ b/tests/ui/crate_level_checks/entrypoint_recursion.stderr @@ -1,5 +1,5 @@ error: recursing into entrypoint `a` - --> $DIR/entrypoint_recursion.rs:11:5 + --> $DIR/entrypoint_recursion.rs:10:5 | LL | a(); | ^ diff --git a/tests/ui/declare_interior_mutable_const/others.rs b/tests/ui/declare_interior_mutable_const/others.rs index 62af545db..896596b56 100644 --- a/tests/ui/declare_interior_mutable_const/others.rs +++ b/tests/ui/declare_interior_mutable_const/others.rs @@ -31,9 +31,25 @@ const NO_ANN: &dyn Display = &70; static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); //^ there should be no lints on this line -// issue #8493 -thread_local! { - static THREAD_LOCAL: Cell = const { Cell::new(0) }; +mod issue_8493 { + use std::cell::Cell; + + thread_local! { + static _BAR: Cell = const { Cell::new(0) }; + } + + macro_rules! issue_8493 { + () => { + const _BAZ: Cell = Cell::new(0); //~ ERROR interior mutable + static _FOOBAR: () = { + thread_local! { + static _VAR: Cell = const { Cell::new(0) }; + } + }; + }; + } + + issue_8493!(); } fn main() {} diff --git a/tests/ui/declare_interior_mutable_const/others.stderr b/tests/ui/declare_interior_mutable_const/others.stderr index fd0689dfc..1fd6d7322 100644 --- a/tests/ui/declare_interior_mutable_const/others.stderr +++ b/tests/ui/declare_interior_mutable_const/others.stderr @@ -35,5 +35,16 @@ LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable | = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors +error: a `const` item should never be interior mutable + --> $DIR/others.rs:43:13 + | +LL | const _BAZ: Cell = Cell::new(0); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | issue_8493!(); + | ------------- in this macro invocation + | + = note: this error originates in the macro `issue_8493` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 5 previous errors diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index b276c384c..c629c0e53 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -1,7 +1,6 @@ #![allow(dead_code)] #![warn(clippy::expl_impl_clone_on_copy)] - #[derive(Copy)] struct Qux; diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index 82a70ceec..fabfd0253 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -1,5 +1,5 @@ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:8:1 + --> $DIR/derive.rs:7:1 | LL | / impl Clone for Qux { LL | | fn clone(&self) -> Self { @@ -10,7 +10,7 @@ LL | | } | = note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings` note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:8:1 + --> $DIR/derive.rs:7:1 | LL | / impl Clone for Qux { LL | | fn clone(&self) -> Self { @@ -20,7 +20,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:32:1 + --> $DIR/derive.rs:31:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | fn clone(&self) -> Self { @@ -30,7 +30,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:32:1 + --> $DIR/derive.rs:31:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | fn clone(&self) -> Self { @@ -40,7 +40,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:43:1 + --> $DIR/derive.rs:42:1 | LL | / impl Clone for BigArray { LL | | fn clone(&self) -> Self { @@ -50,7 +50,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:43:1 + --> $DIR/derive.rs:42:1 | LL | / impl Clone for BigArray { LL | | fn clone(&self) -> Self { @@ -60,7 +60,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:54:1 + --> $DIR/derive.rs:53:1 | LL | / impl Clone for FnPtr { LL | | fn clone(&self) -> Self { @@ -70,7 +70,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:54:1 + --> $DIR/derive.rs:53:1 | LL | / impl Clone for FnPtr { LL | | fn clone(&self) -> Self { @@ -80,7 +80,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:74:1 + --> $DIR/derive.rs:73:1 | LL | / impl Clone for Generic2 { LL | | fn clone(&self) -> Self { @@ -90,7 +90,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:74:1 + --> $DIR/derive.rs:73:1 | LL | / impl Clone for Generic2 { LL | | fn clone(&self) -> Self { diff --git a/tests/ui/expect_tool_lint_rfc_2383.rs b/tests/ui/expect_tool_lint_rfc_2383.rs index 28b37f96e..985835ffa 100644 --- a/tests/ui/expect_tool_lint_rfc_2383.rs +++ b/tests/ui/expect_tool_lint_rfc_2383.rs @@ -22,9 +22,9 @@ mod rustc_ok { #[expect(illegal_floating_point_literal_pattern)] match x { - 5.0 => {} - 6.0 => {} - _ => {} + 5.0 => {}, + 6.0 => {}, + _ => {}, } } } @@ -38,9 +38,9 @@ mod rustc_warn { #[expect(illegal_floating_point_literal_pattern)] match x { - 5 => {} - 6 => {} - _ => {} + 5 => {}, + 6 => {}, + _ => {}, } } } diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index d08f8f524..f4db2d20c 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -84,4 +84,10 @@ fn main() { let _ = x.to_string(); let _ = format!("{x:?}"); // Don't lint on debug let _ = x.to_string(); + + // Issue #9234 + let abc = "abc"; + let _ = abc.to_string(); + let xx = "xx"; + let _ = xx.to_string(); } diff --git a/tests/ui/format.rs b/tests/ui/format.rs index 4a10b580d..bf687cb1e 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -86,4 +86,10 @@ fn main() { let _ = format!("{x}"); let _ = format!("{x:?}"); // Don't lint on debug let _ = format!("{y}", y = x); + + // Issue #9234 + let abc = "abc"; + let _ = format!("{abc}"); + let xx = "xx"; + let _ = format!("{xx}"); } diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index f25c7fb1f..a0f8e7d19 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -111,5 +111,17 @@ error: useless use of `format!` LL | let _ = format!("{y}", y = x); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` -error: aborting due to 17 previous errors +error: useless use of `format!` + --> $DIR/format.rs:92:13 + | +LL | let _ = format!("{abc}"); + | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()` + +error: useless use of `format!` + --> $DIR/format.rs:94:13 + | +LL | let _ = format!("{xx}"); + | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()` + +error: aborting due to 19 previous errors diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index dbfeb4379..7aba5b447 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -1,5 +1,5 @@ #![warn(clippy::match_same_arms)] -#![allow(clippy::blacklisted_name)] +#![allow(clippy::blacklisted_name, clippy::diverging_sub_expression)] fn bar(_: T) {} fn foo() -> bool { @@ -227,4 +227,12 @@ fn main() { Some(Bar { y: 0, x: 5, .. }) => 1, _ => 200, }; + + let _ = match 0 { + 0 => todo!(), + 1 => todo!(), + 2 => core::convert::identity::(todo!()), + 3 => core::convert::identity::(todo!()), + _ => 5, + }; } diff --git a/tests/ui/mismatching_type_param_order.rs b/tests/ui/mismatching_type_param_order.rs index 8f286c930..8c0da84d8 100644 --- a/tests/ui/mismatching_type_param_order.rs +++ b/tests/ui/mismatching_type_param_order.rs @@ -57,4 +57,8 @@ fn main() { B: Copy, { } + + // if the types are complicated, do not lint + impl Foo<(K, V), B> {} + impl Foo<(K, V), A> {} } diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 09afe2ddb..bfd2725ec 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -158,3 +158,28 @@ fn check_expect_suppression() { #[expect(clippy::needless_borrow)] let _ = x(&&a); } + +#[allow(dead_code)] +mod issue9160 { + pub struct S { + f: F, + } + + impl S + where + F: Fn() -> T, + { + fn calls_field(&self) -> T { + (self.f)() + } + } + + impl S + where + F: FnMut() -> T, + { + fn calls_mut_field(&mut self) -> T { + (self.f)() + } + } +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 3ae4722a1..c457d8c54 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -158,3 +158,28 @@ fn check_expect_suppression() { #[expect(clippy::needless_borrow)] let _ = x(&&a); } + +#[allow(dead_code)] +mod issue9160 { + pub struct S { + f: F, + } + + impl S + where + F: Fn() -> T, + { + fn calls_field(&self) -> T { + (&self.f)() + } + } + + impl S + where + F: FnMut() -> T, + { + fn calls_mut_field(&mut self) -> T { + (&mut self.f)() + } + } +} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 8a2e2b989..66588689d 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -120,5 +120,17 @@ error: this expression creates a reference which is immediately dereferenced by LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` -error: aborting due to 20 previous errors +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:173:13 + | +LL | (&self.f)() + | ^^^^^^^^^ help: change this to: `(self.f)` + +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:182:13 + | +LL | (&mut self.f)() + | ^^^^^^^^^^^^^ help: change this to: `(self.f)` + +error: aborting due to 22 previous errors diff --git a/tests/ui/obfuscated_if_else.fixed b/tests/ui/obfuscated_if_else.fixed new file mode 100644 index 000000000..62d932c2c --- /dev/null +++ b/tests/ui/obfuscated_if_else.fixed @@ -0,0 +1,7 @@ +// run-rustfix + +#![warn(clippy::obfuscated_if_else)] + +fn main() { + if true { "a" } else { "b" }; +} diff --git a/tests/ui/obfuscated_if_else.rs b/tests/ui/obfuscated_if_else.rs new file mode 100644 index 000000000..273be9092 --- /dev/null +++ b/tests/ui/obfuscated_if_else.rs @@ -0,0 +1,7 @@ +// run-rustfix + +#![warn(clippy::obfuscated_if_else)] + +fn main() { + true.then_some("a").unwrap_or("b"); +} diff --git a/tests/ui/obfuscated_if_else.stderr b/tests/ui/obfuscated_if_else.stderr new file mode 100644 index 000000000..e4180c288 --- /dev/null +++ b/tests/ui/obfuscated_if_else.stderr @@ -0,0 +1,10 @@ +error: use of `.then_some(..).unwrap_or(..)` can be written more clearly with `if .. else ..` + --> $DIR/obfuscated_if_else.rs:6:5 + | +LL | true.then_some("a").unwrap_or("b"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { "a" } else { "b" }` + | + = note: `-D clippy::obfuscated-if-else` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index 74f05ec1f..6b27475de 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -9,6 +9,8 @@ fn std_instead_of_core() { use std::hash::Hasher; // Absolute path use ::std::hash::Hash; + // Don't lint on `env` macro + use std::env; // Multiple imports use std::fmt::{Debug, Result}; @@ -20,10 +22,14 @@ fn std_instead_of_core() { // Types let cell = std::cell::Cell::new(8u32); let cell_absolute = ::std::cell::Cell::new(8u32); + + let _ = std::env!("PATH"); } #[warn(clippy::std_instead_of_alloc)] fn std_instead_of_alloc() { + // Only lint once. + use std::vec; use std::vec::Vec; } diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index 9f1644835..bc49dabf5 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr @@ -16,7 +16,7 @@ LL | use ::std::hash::Hash; = help: consider importing the item from `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:14:20 + --> $DIR/std_instead_of_core.rs:16:20 | LL | use std::fmt::{Debug, Result}; | ^^^^^ @@ -24,7 +24,7 @@ LL | use std::fmt::{Debug, Result}; = help: consider importing the item from `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:14:27 + --> $DIR/std_instead_of_core.rs:16:27 | LL | use std::fmt::{Debug, Result}; | ^^^^^^ @@ -32,7 +32,7 @@ LL | use std::fmt::{Debug, Result}; = help: consider importing the item from `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:17:15 + --> $DIR/std_instead_of_core.rs:19:15 | LL | let ptr = std::ptr::null::(); | ^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let ptr = std::ptr::null::(); = help: consider importing the item from `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:18:19 + --> $DIR/std_instead_of_core.rs:20:19 | LL | let ptr_mut = ::std::ptr::null_mut::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let ptr_mut = ::std::ptr::null_mut::(); = help: consider importing the item from `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:21:16 + --> $DIR/std_instead_of_core.rs:23:16 | LL | let cell = std::cell::Cell::new(8u32); | ^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let cell = std::cell::Cell::new(8u32); = help: consider importing the item from `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:22:25 + --> $DIR/std_instead_of_core.rs:24:25 | LL | let cell_absolute = ::std::cell::Cell::new(8u32); | ^^^^^^^^^^^^^^^^^ @@ -64,16 +64,24 @@ LL | let cell_absolute = ::std::cell::Cell::new(8u32); = help: consider importing the item from `core` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:27:9 + --> $DIR/std_instead_of_core.rs:32:9 | -LL | use std::vec::Vec; - | ^^^^^^^^^^^^^ +LL | use std::vec; + | ^^^^^^^^ | = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` = help: consider importing the item from `alloc` +error: used import from `std` instead of `alloc` + --> $DIR/std_instead_of_core.rs:33:9 + | +LL | use std::vec::Vec; + | ^^^^^^^^^^^^^ + | + = help: consider importing the item from `alloc` + error: used import from `alloc` instead of `core` - --> $DIR/std_instead_of_core.rs:32:9 + --> $DIR/std_instead_of_core.rs:38:9 | LL | use alloc::slice::from_ref; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -81,5 +89,5 @@ LL | use alloc::slice::from_ref; = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` = help: consider importing the item from `core` -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/temporary_assignment.rs b/tests/ui/temporary_assignment.rs index b4a931043..ac4c1bc65 100644 --- a/tests/ui/temporary_assignment.rs +++ b/tests/ui/temporary_assignment.rs @@ -1,5 +1,4 @@ #![warn(clippy::temporary_assignment)] -#![allow(const_item_mutation)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/temporary_assignment.stderr b/tests/ui/temporary_assignment.stderr index 4cc32c79f..7d79901a2 100644 --- a/tests/ui/temporary_assignment.stderr +++ b/tests/ui/temporary_assignment.stderr @@ -1,5 +1,5 @@ error: assignment to temporary - --> $DIR/temporary_assignment.rs:48:5 + --> $DIR/temporary_assignment.rs:47:5 | LL | Struct { field: 0 }.field = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1; = note: `-D clippy::temporary-assignment` implied by `-D warnings` error: assignment to temporary - --> $DIR/temporary_assignment.rs:49:5 + --> $DIR/temporary_assignment.rs:48:5 | LL | / MultiStruct { LL | | structure: Struct { field: 0 }, @@ -17,13 +17,13 @@ LL | | .field = 1; | |______________^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:54:5 + --> $DIR/temporary_assignment.rs:53:5 | LL | ArrayStruct { array: [0] }.array[0] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:55:5 + --> $DIR/temporary_assignment.rs:54:5 | LL | (0, 0).0 = 1; | ^^^^^^^^^^^^ diff --git a/tests/ui/unused_self.rs b/tests/ui/unused_self.rs index 08bf58fec..92e8e1dba 100644 --- a/tests/ui/unused_self.rs +++ b/tests/ui/unused_self.rs @@ -53,8 +53,17 @@ mod unused_self_allow { // shouldn't trigger fn unused_self_move(self) {} } + + pub struct D; + + impl D { + // shouldn't trigger for public methods + pub fn unused_self_move(self) {} + } } +pub use unused_self_allow::D; + mod used_self { use std::pin::Pin;