diff --git a/Cargo.lock b/Cargo.lock index bdfb51761760f..c83763be3d893 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -613,9 +613,8 @@ dependencies = [ [[package]] name = "clippy" -version = "0.1.61" +version = "0.1.62" dependencies = [ - "cargo_metadata", "clippy_lints", "clippy_utils", "compiletest_rs", @@ -642,20 +641,18 @@ dependencies = [ name = "clippy_dev" version = "0.0.1" dependencies = [ - "bytecount", - "cargo_metadata", "clap 2.34.0", "indoc", "itertools", "opener", - "regex", "shell-escape", + "tempfile", "walkdir", ] [[package]] name = "clippy_lints" -version = "0.1.61" +version = "0.1.62" dependencies = [ "cargo_metadata", "clippy_utils", @@ -676,7 +673,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.61" +version = "0.1.62" dependencies = [ "arrayvec", "if_chain", diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 88f71931d92b5..b4097ea86a518 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,143 @@ document. ## Unreleased / In Rust Nightly -[0eff589...master](https://github.com/rust-lang/rust-clippy/compare/0eff589...master) +[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master) -## Rust 1.59 (beta) +## Rust 1.60 -Current beta, release 2022-02-24 +Current stable, released 2022-04-07 + +[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b) + +### New Lints + +* [`single_char_lifetime_names`] + [#8236](https://github.com/rust-lang/rust-clippy/pull/8236) +* [`iter_overeager_cloned`] + [#8203](https://github.com/rust-lang/rust-clippy/pull/8203) +* [`transmute_undefined_repr`] + [#8398](https://github.com/rust-lang/rust-clippy/pull/8398) +* [`default_union_representation`] + [#8289](https://github.com/rust-lang/rust-clippy/pull/8289) +* [`manual_bits`] + [#8213](https://github.com/rust-lang/rust-clippy/pull/8213) +* [`borrow_as_ptr`] + [#8210](https://github.com/rust-lang/rust-clippy/pull/8210) + +### Moves and Deprecations + +* Moved [`disallowed_methods`] and [`disallowed_types`] to `style` (now warn-by-default) + [#8261](https://github.com/rust-lang/rust-clippy/pull/8261) +* Rename `ref_in_deref` to [`needless_borrow`] + [#8217](https://github.com/rust-lang/rust-clippy/pull/8217) +* Moved [`mutex_atomic`] to `nursery` (now allow-by-default) + [#8260](https://github.com/rust-lang/rust-clippy/pull/8260) + +### Enhancements + +* [`ptr_arg`]: Now takes the argument usage into account and lints for mutable references + [#8271](https://github.com/rust-lang/rust-clippy/pull/8271) +* [`unused_io_amount`]: Now supports async read and write traits + [#8179](https://github.com/rust-lang/rust-clippy/pull/8179) +* [`while_let_on_iterator`]: Improved detection to catch more cases + [#8221](https://github.com/rust-lang/rust-clippy/pull/8221) +* [`trait_duplication_in_bounds`]: Now covers trait functions with `Self` bounds + [#8252](https://github.com/rust-lang/rust-clippy/pull/8252) +* [`unwrap_used`]: Now works for `.get(i).unwrap()` and `.get_mut(i).unwrap()` + [#8372](https://github.com/rust-lang/rust-clippy/pull/8372) +* [`map_clone`]: The suggestion takes `msrv` into account + [#8280](https://github.com/rust-lang/rust-clippy/pull/8280) +* [`manual_bits`] and [`borrow_as_ptr`]: Now track the `clippy::msrv` attribute + [#8280](https://github.com/rust-lang/rust-clippy/pull/8280) +* [`disallowed_methods`]: Now works for methods on primitive types + [#8112](https://github.com/rust-lang/rust-clippy/pull/8112) +* [`not_unsafe_ptr_arg_deref`]: Now works for type aliases + [#8273](https://github.com/rust-lang/rust-clippy/pull/8273) +* [`needless_question_mark`]: Now works for async functions + [#8311](https://github.com/rust-lang/rust-clippy/pull/8311) +* [`iter_not_returning_iterator`]: Now handles type projections + [#8228](https://github.com/rust-lang/rust-clippy/pull/8228) +* [`wrong_self_convention`]: Now detects wrong `self` references in more cases + [#8208](https://github.com/rust-lang/rust-clippy/pull/8208) +* [`single_match`]: Now works for `match` statements with tuples + [#8322](https://github.com/rust-lang/rust-clippy/pull/8322) + +### False Positive Fixes + +* [`erasing_op`]: No longer triggers if the output type changes + [#8204](https://github.com/rust-lang/rust-clippy/pull/8204) +* [`if_same_then_else`]: No longer triggers for `if let` statements + [#8297](https://github.com/rust-lang/rust-clippy/pull/8297) +* [`manual_memcpy`]: No longer lints on `VecDeque` + [#8226](https://github.com/rust-lang/rust-clippy/pull/8226) +* [`trait_duplication_in_bounds`]: Now takes path segments into account + [#8315](https://github.com/rust-lang/rust-clippy/pull/8315) +* [`deref_addrof`]: No longer lints when the dereference or borrow occurs in different a context + [#8268](https://github.com/rust-lang/rust-clippy/pull/8268) +* [`type_repetition_in_bounds`]: Now checks for full equality to prevent false positives + [#8224](https://github.com/rust-lang/rust-clippy/pull/8224) +* [`ptr_arg`]: No longer lint for mutable references in traits + [#8369](https://github.com/rust-lang/rust-clippy/pull/8369) +* [`implicit_clone`]: No longer lints for double references + [#8231](https://github.com/rust-lang/rust-clippy/pull/8231) +* [`needless_lifetimes`]: No longer lints lifetimes for explicit `self` types + [#8278](https://github.com/rust-lang/rust-clippy/pull/8278) +* [`op_ref`]: No longer lints in `BinOp` impl if that can cause recursion + [#8298](https://github.com/rust-lang/rust-clippy/pull/8298) +* [`enum_variant_names`]: No longer triggers for empty variant names + [#8329](https://github.com/rust-lang/rust-clippy/pull/8329) +* [`redundant_closure`]: No longer lints for `Arc` or `Rc` + [#8193](https://github.com/rust-lang/rust-clippy/pull/8193) +* [`iter_not_returning_iterator`]: No longer lints on trait implementations but therefore on trait definitions + [#8228](https://github.com/rust-lang/rust-clippy/pull/8228) +* [`single_match`]: No longer lints on exhaustive enum patterns without a wildcard + [#8322](https://github.com/rust-lang/rust-clippy/pull/8322) +* [`manual_swap`]: No longer lints on cases that involve automatic dereferences + [#8220](https://github.com/rust-lang/rust-clippy/pull/8220) +* [`useless_format`]: Now works for implicit named arguments + [#8295](https://github.com/rust-lang/rust-clippy/pull/8295) + +### Suggestion Fixes/Improvements + +* [`needless_borrow`]: Prevent mutable borrows being moved and suggest removing the borrow on method calls + [#8217](https://github.com/rust-lang/rust-clippy/pull/8217) +* [`chars_next_cmp`]: Correctly excapes the suggestion + [#8376](https://github.com/rust-lang/rust-clippy/pull/8376) +* [`explicit_write`]: Add suggestions for `write!`s with format arguments + [#8365](https://github.com/rust-lang/rust-clippy/pull/8365) +* [`manual_memcpy`]: Suggests `copy_from_slice` when applicable + [#8226](https://github.com/rust-lang/rust-clippy/pull/8226) +* [`or_fun_call`]: Improved suggestion display for long arguments + [#8292](https://github.com/rust-lang/rust-clippy/pull/8292) +* [`unnecessary_cast`]: Now correctly includes the sign + [#8350](https://github.com/rust-lang/rust-clippy/pull/8350) +* [`cmp_owned`]: No longer flips the comparison order + [#8299](https://github.com/rust-lang/rust-clippy/pull/8299) +* [`explicit_counter_loop`]: Now correctly suggests `iter()` on references + [#8382](https://github.com/rust-lang/rust-clippy/pull/8382) + +### ICE Fixes + +* [`manual_split_once`] + [#8250](https://github.com/rust-lang/rust-clippy/pull/8250) + +### Documentation Improvements + +* [`map_flatten`]: Add documentation for the `Option` type + [#8354](https://github.com/rust-lang/rust-clippy/pull/8354) +* Document that Clippy's driver might use a different code generation than rustc + [#8037](https://github.com/rust-lang/rust-clippy/pull/8037) +* Clippy's lint list will now automatically focus the search box + [#8343](https://github.com/rust-lang/rust-clippy/pull/8343) + +### Others + +* Clippy now warns if we find multiple Clippy config files exist + [#8326](https://github.com/rust-lang/rust-clippy/pull/8326) + +## Rust 1.59 + +Released 2022-02-24 [e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589) @@ -174,7 +306,7 @@ Current beta, release 2022-02-24 ## Rust 1.58 -Current stable, released 2022-01-13 +Released 2022-01-13 [00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011) @@ -3069,6 +3201,7 @@ Released 2018-09-13 [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons +[`cast_abs_to_unsigned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned [`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor [`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless @@ -3097,6 +3230,7 @@ Released 2018-09-13 [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator +[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def [`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute [`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro @@ -3123,6 +3257,7 @@ Released 2018-09-13 [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens [`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy +[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec @@ -3130,12 +3265,14 @@ Released 2018-09-13 [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop +[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets [`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant [`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use [`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names [`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op [`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op +[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums @@ -3174,6 +3311,7 @@ Released 2018-09-13 [`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map [`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy +[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 123af23881b62..dd6518d5241b5 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.61" +version = "0.1.62" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -21,13 +21,12 @@ name = "clippy-driver" path = "src/driver.rs" [dependencies] -clippy_lints = { version = "0.1", path = "clippy_lints" } +clippy_lints = { path = "clippy_lints" } semver = "1.0" -rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } +rustc_tools_util = { path = "rustc_tools_util" } tempfile = { version = "3.2", optional = true } [dev-dependencies] -cargo_metadata = "0.14" compiletest_rs = { version = "0.7.1", features = ["tmp"] } tester = "0.9" regex = "1.5" @@ -45,7 +44,7 @@ derive-new = "0.5" if_chain = "1.0" itertools = "0.10.1" quote = "1.0" -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0.125", features = ["derive"] } syn = { version = "1.0", features = ["full"] } futures = "0.3" parking_lot = "0.11.2" diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index d133e8cddabc7..81faa5fe5e148 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -4,15 +4,17 @@ version = "0.0.1" edition = "2021" [dependencies] -bytecount = "0.6" clap = "2.33" indoc = "1.0" itertools = "0.10.1" opener = "0.5" -regex = "1.5" shell-escape = "0.1" +tempfile = "3.2" walkdir = "2.3" -cargo_metadata = "0.14" [features] deny-warnings = [] + +[package.metadata.rust-analyzer] +# This package uses #[feature(rustc_private)] +rustc_private = true diff --git a/src/tools/clippy/clippy_dev/src/bless.rs b/src/tools/clippy/clippy_dev/src/bless.rs index b0fb39e816996..8e5c739afe05a 100644 --- a/src/tools/clippy/clippy_dev/src/bless.rs +++ b/src/tools/clippy/clippy_dev/src/bless.rs @@ -1,22 +1,15 @@ //! `bless` updates the reference files in the repo with changed output files //! from the last test run. +use crate::cargo_clippy_path; use std::ffi::OsStr; use std::fs; use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; -#[cfg(not(windows))] -static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; -#[cfg(windows)] -static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; - -static CLIPPY_BUILD_TIME: SyncLazy> = SyncLazy::new(|| { - let mut path = std::env::current_exe().unwrap(); - path.set_file_name(CARGO_CLIPPY_EXE); - fs::metadata(path).ok()?.modified().ok() -}); +static CLIPPY_BUILD_TIME: SyncLazy> = + SyncLazy::new(|| cargo_clippy_path().metadata().ok()?.modified().ok()); /// # Panics /// diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 59fde44754714..9c6d754b400fc 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,8 +1,12 @@ +#![feature(let_else)] #![feature(once_cell)] +#![feature(rustc_private)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] +extern crate rustc_lexer; + use std::path::PathBuf; pub mod bless; @@ -13,6 +17,19 @@ pub mod serve; pub mod setup; pub mod update_lints; +#[cfg(not(windows))] +static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; +#[cfg(windows)] +static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; + +/// Returns the path to the `cargo-clippy` binary +#[must_use] +pub fn cargo_clippy_path() -> PathBuf { + let mut path = std::env::current_exe().expect("failed to get current executable name"); + path.set_file_name(CARGO_CLIPPY_EXE); + path +} + /// Returns the path to the Clippy project directory /// /// # Panics diff --git a/src/tools/clippy/clippy_dev/src/lint.rs b/src/tools/clippy/clippy_dev/src/lint.rs index b8287980a4bac..1bc1a39542db5 100644 --- a/src/tools/clippy/clippy_dev/src/lint.rs +++ b/src/tools/clippy/clippy_dev/src/lint.rs @@ -1,19 +1,52 @@ -use std::process::{self, Command}; +use crate::cargo_clippy_path; +use std::process::{self, Command, ExitStatus}; +use std::{fs, io}; -pub fn run(filename: &str) { - let code = Command::new("cargo") - .args(["run", "--bin", "clippy-driver", "--"]) - .args(["-L", "./target/debug"]) - .args(["-Z", "no-codegen"]) - .args(["--edition", "2021"]) - .arg(filename) - .status() - .expect("failed to run cargo") - .code(); - - if code.is_none() { - eprintln!("Killed by signal"); +fn exit_if_err(status: io::Result) { + match status.expect("failed to run command").code() { + Some(0) => {}, + Some(n) => process::exit(n), + None => { + eprintln!("Killed by signal"); + process::exit(1); + }, } +} + +pub fn run(path: &str) { + let is_file = match fs::metadata(path) { + Ok(metadata) => metadata.is_file(), + Err(e) => { + eprintln!("Failed to read {path}: {e:?}"); + process::exit(1); + }, + }; + + if is_file { + exit_if_err( + Command::new("cargo") + .args(["run", "--bin", "clippy-driver", "--"]) + .args(["-L", "./target/debug"]) + .args(["-Z", "no-codegen"]) + .args(["--edition", "2021"]) + .arg(path) + .status(), + ); + } else { + exit_if_err(Command::new("cargo").arg("build").status()); - process::exit(code.unwrap_or(1)); + // Run in a tempdir as changes to clippy do not retrigger linting + let target = tempfile::Builder::new() + .prefix("clippy") + .tempdir() + .expect("failed to create tempdir"); + + let status = Command::new(cargo_clippy_path()) + .current_dir(path) + .env("CARGO_TARGET_DIR", target.as_ref()) + .status(); + + target.close().expect("failed to remove tempdir"); + exit_if_err(status); + } } diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 30a241c8ba151..b1fe35a0243f0 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -4,6 +4,7 @@ use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints}; +use indoc::indoc; fn main() { let matches = get_clap_config(); @@ -56,8 +57,8 @@ fn main() { serve::run(port, lint); }, ("lint", Some(matches)) => { - let filename = matches.value_of("filename").unwrap(); - lint::run(filename); + let path = matches.value_of("path").unwrap(); + lint::run(path); }, _ => {}, } @@ -225,11 +226,20 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { ) .subcommand( SubCommand::with_name("lint") - .about("Manually run clippy on a file") + .about("Manually run clippy on a file or package") + .after_help(indoc! {" + EXAMPLES + Lint a single file: + cargo dev lint tests/ui/attrs.rs + + Lint a package directory: + cargo dev lint tests/ui-cargo/wildcard_dependencies/fail + cargo dev lint ~/my-project + "}) .arg( - Arg::with_name("filename") + Arg::with_name("path") .required(true) - .help("The path to a file to lint"), + .help("The path to a file or package directory to lint"), ), ) .get_matches() diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 59658b42c79b1..7a3fd1317619e 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -133,15 +133,23 @@ fn to_camel_case(name: &str) -> String { } fn get_stabilisation_version() -> String { - let mut command = cargo_metadata::MetadataCommand::new(); - command.no_deps(); - if let Ok(metadata) = command.exec() { - if let Some(pkg) = metadata.packages.iter().find(|pkg| pkg.name == "clippy") { - return format!("{}.{}.0", pkg.version.minor, pkg.version.patch); - } + fn parse_manifest(contents: &str) -> Option { + let version = contents + .lines() + .filter_map(|l| l.split_once('=')) + .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?; + let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else { + return None; + }; + let (minor, patch) = version.split_once('.')?; + Some(format!( + "{}.{}.0", + minor.parse::().ok()?, + patch.parse::().ok()? + )) } - - String::from("") + let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`"); + parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`") } fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index d368ef1f46a2a..59db51fbfac51 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -1,9 +1,9 @@ +use core::fmt::Write; use itertools::Itertools; -use regex::Regex; +use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use std::collections::HashMap; use std::ffi::OsStr; use std::fs; -use std::lazy::SyncLazy; use std::path::Path; use walkdir::WalkDir; @@ -13,35 +13,7 @@ const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev u // Use that command to update this file and do not edit by hand.\n\ // Manual edits will be overwritten.\n\n"; -static DEC_CLIPPY_LINT_RE: SyncLazy = SyncLazy::new(|| { - Regex::new( - r#"(?x) - declare_clippy_lint!\s*[\{(] - (?:\s+///.*)* - (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])? - \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* - (?P[a-z_]+)\s*,\s* - "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] -"#, - ) - .unwrap() -}); - -static DEC_DEPRECATED_LINT_RE: SyncLazy = SyncLazy::new(|| { - Regex::new( - r#"(?x) - declare_deprecated_lint!\s*[{(]\s* - (?:\s+///.*)* - (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])? - \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* - "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] -"#, - ) - .unwrap() -}); -static NL_ESCAPE_RE: SyncLazy = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap()); - -static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; +const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; #[derive(Clone, Copy, PartialEq)] pub enum UpdateMode { @@ -60,60 +32,52 @@ pub enum UpdateMode { /// Panics if a file path could not read from or then written to #[allow(clippy::too_many_lines)] pub fn run(update_mode: UpdateMode) { - let lint_list: Vec = gather_all().collect(); + let (lints, deprecated_lints) = gather_all(); - let internal_lints = Lint::internal_lints(&lint_list); - let deprecated_lints = Lint::deprecated_lints(&lint_list); - let usable_lints = Lint::usable_lints(&lint_list); + let internal_lints = Lint::internal_lints(&lints); + let usable_lints = Lint::usable_lints(&lints); let mut sorted_usable_lints = usable_lints.clone(); sorted_usable_lints.sort_by_key(|lint| lint.name.clone()); - let usable_lint_count = round_to_fifty(usable_lints.len()); - - let mut file_change = false; - - file_change |= replace_region_in_file( + replace_region_in_file( + update_mode, Path::new("README.md"), - &format!( - r#"\[There are over \d+ lints included in this crate!\]\({}\)"#, - DOCS_LINK - ), - "", - true, - update_mode == UpdateMode::Change, - || { - vec![format!( - "[There are over {} lints included in this crate!]({})", - usable_lint_count, DOCS_LINK - )] + "[There are over ", + " lints included in this crate!]", + |res| { + write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap(); }, - ) - .changed; + ); - file_change |= replace_region_in_file( + replace_region_in_file( + update_mode, Path::new("CHANGELOG.md"), - "", + "\n", "", - false, - update_mode == UpdateMode::Change, - || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())), - ) - .changed; + |res| { + for lint in usable_lints + .iter() + .map(|l| &l.name) + .chain(deprecated_lints.iter().map(|l| &l.name)) + .sorted() + { + writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap(); + } + }, + ); // This has to be in lib.rs, otherwise rustfmt doesn't work - file_change |= replace_region_in_file( + replace_region_in_file( + update_mode, Path::new("clippy_lints/src/lib.rs"), - "begin lints modules", - "end lints modules", - false, - update_mode == UpdateMode::Change, - || gen_modules_list(usable_lints.iter()), - ) - .changed; - - if file_change && update_mode == UpdateMode::Check { - exit_with_failure(); - } + "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n", + "// end lints modules, do not remove this comment, it’s used in `update_lints`", + |res| { + for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() { + writeln!(res, "mod {};", lint_mod).unwrap(); + } + }, + ); process_file( "clippy_lints/src/lib.register_lints.rs", @@ -123,7 +87,7 @@ pub fn run(update_mode: UpdateMode) { process_file( "clippy_lints/src/lib.deprecated.rs", update_mode, - &gen_deprecated(deprecated_lints.iter()), + &gen_deprecated(&deprecated_lints), ); let all_group_lints = usable_lints.iter().filter(|l| { @@ -146,15 +110,12 @@ pub fn run(update_mode: UpdateMode) { } pub fn print_lints() { - let lint_list: Vec = gather_all().collect(); + let (lint_list, _) = gather_all(); let usable_lints = Lint::usable_lints(&lint_list); let usable_lint_count = usable_lints.len(); let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); for (lint_group, mut lints) in grouped_by_lint_group { - if lint_group == "Deprecated" { - continue; - } println!("\n## {}", lint_group); lints.sort_by_key(|l| l.name.clone()); @@ -198,19 +159,17 @@ struct Lint { name: String, group: String, desc: String, - deprecation: Option, module: String, } impl Lint { #[must_use] - fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self { + fn new(name: &str, group: &str, desc: &str, module: &str) -> Self { Self { name: name.to_lowercase(), - group: group.to_string(), - desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(), - deprecation: deprecation.map(ToString::to_string), - module: module.to_string(), + group: group.into(), + desc: remove_line_splices(desc), + module: module.into(), } } @@ -219,7 +178,7 @@ impl Lint { fn usable_lints(lints: &[Self]) -> Vec { lints .iter() - .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal")) + .filter(|l| !l.group.starts_with("internal")) .cloned() .collect() } @@ -230,12 +189,6 @@ impl Lint { lints.iter().filter(|l| l.group == "internal").cloned().collect() } - /// Returns all deprecated lints - #[must_use] - fn deprecated_lints(lints: &[Self]) -> Vec { - lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect() - } - /// Returns the lints in a `HashMap`, grouped by the different lint groups #[must_use] fn by_lint_group(lints: impl Iterator) -> HashMap> { @@ -243,6 +196,20 @@ impl Lint { } } +#[derive(Clone, PartialEq, Debug)] +struct DeprecatedLint { + name: String, + reason: String, +} +impl DeprecatedLint { + fn new(name: &str, reason: &str) -> Self { + Self { + name: name.to_lowercase(), + reason: remove_line_splices(reason), + } + } +} + /// Generates the code for registering a group fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator) -> String { let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect(); @@ -262,32 +229,12 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator(lints: impl Iterator) -> Vec { - lints - .map(|l| &l.module) - .unique() - .map(|module| format!("mod {};", module)) - .sorted() - .collect::>() -} - -/// Generates the list of lint links at the bottom of the CHANGELOG -#[must_use] -fn gen_changelog_lint_list<'a>(lints: impl Iterator) -> Vec { - lints - .sorted_by_key(|l| &l.name) - .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name)) - .collect() -} - /// Generates the `register_removed` code #[must_use] -fn gen_deprecated<'a>(lints: impl Iterator) -> String { +fn gen_deprecated(lints: &[DeprecatedLint]) -> String { let mut output = GENERATED_FILE_COMMENT.to_string(); output.push_str("{\n"); - for Lint { name, deprecation, .. } in lints { + for lint in lints { output.push_str(&format!( concat!( " store.register_removed(\n", @@ -295,8 +242,7 @@ fn gen_deprecated<'a>(lints: impl Iterator) -> String { " \"{}\",\n", " );\n" ), - name, - deprecation.as_ref().expect("`lints` are deprecated") + lint.name, lint.reason, )); } output.push_str("}\n"); @@ -330,61 +276,136 @@ fn gen_register_lint_list<'a>( output } -/// Gathers all files in `src/clippy_lints` and gathers all lints inside -fn gather_all() -> impl Iterator { - lint_files().flat_map(|f| gather_from_file(&f)) -} +/// Gathers all lints defined in `clippy_lints/src` +fn gather_all() -> (Vec, Vec) { + let mut lints = Vec::with_capacity(1000); + let mut deprecated_lints = Vec::with_capacity(50); + let root_path = clippy_project_root().join("clippy_lints/src"); -fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator { - let content = fs::read_to_string(dir_entry.path()).unwrap(); - let path = dir_entry.path(); - let filename = path.file_stem().unwrap(); - let path_buf = path.with_file_name(filename); - let mut rel_path = path_buf - .strip_prefix(clippy_project_root().join("clippy_lints/src")) - .expect("only files in `clippy_lints/src` should be looked at"); - // If the lints are stored in mod.rs, we get the module name from - // the containing directory: - if filename == "mod" { - rel_path = rel_path.parent().unwrap(); - } + for (rel_path, file) in WalkDir::new(&root_path) + .into_iter() + .map(Result::unwrap) + .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) + .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) + { + let path = file.path(); + let contents = + fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); + let module = rel_path + .components() + .map(|c| c.as_os_str().to_str().unwrap()) + .collect::>() + .join("::"); + + // If the lints are stored in mod.rs, we get the module name from + // the containing directory: + let module = if let Some(module) = module.strip_suffix("::mod.rs") { + module + } else { + module.strip_suffix(".rs").unwrap_or(&module) + }; - let module = rel_path - .components() - .map(|c| c.as_os_str().to_str().unwrap()) - .collect::>() - .join("::"); + if module == "deprecated_lints" { + parse_deprecated_contents(&contents, &mut deprecated_lints); + } else { + parse_contents(&contents, module, &mut lints); + } + } + (lints, deprecated_lints) +} - parse_contents(&content, &module) +macro_rules! match_tokens { + ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => { + { + $($(let $capture =)? if let Some((TokenKind::$token $({$($fields)*})?, _x)) = $iter.next() { + _x + } else { + continue; + };)* + #[allow(clippy::unused_unit)] + { ($($($capture,)?)*) } + } + } } -fn parse_contents(content: &str, module: &str) -> impl Iterator { - let lints = DEC_CLIPPY_LINT_RE - .captures_iter(content) - .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module)); - let deprecated = DEC_DEPRECATED_LINT_RE - .captures_iter(content) - .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module)); - // Removing the `.collect::>().into_iter()` causes some lifetime issues due to the map - lints.chain(deprecated).collect::>().into_iter() +/// Parse a source file looking for `declare_clippy_lint` macro invocations. +fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { + let mut offset = 0usize; + let mut iter = tokenize(contents).map(|t| { + let range = offset..offset + t.len; + offset = range.end; + (t.kind, &contents[range]) + }); + + while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_clippy_lint") { + let mut iter = iter + .by_ref() + .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); + // matches `!{` + match_tokens!(iter, Bang OpenBrace); + match iter.next() { + // #[clippy::version = "version"] pub + Some((TokenKind::Pound, _)) => { + match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident); + }, + // pub + Some((TokenKind::Ident, _)) => (), + _ => continue, + } + let (name, group, desc) = match_tokens!( + iter, + // LINT_NAME + Ident(name) Comma + // group, + Ident(group) Comma + // "description" } + Literal{..}(desc) CloseBrace + ); + lints.push(Lint::new(name, group, desc, module)); + } } -/// Collects all .rs files in the `clippy_lints/src` directory -fn lint_files() -> impl Iterator { - // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories. - // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`. - let path = clippy_project_root().join("clippy_lints/src"); - WalkDir::new(path) - .into_iter() - .filter_map(Result::ok) - .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) +/// Parse a source file looking for `declare_deprecated_lint` macro invocations. +fn parse_deprecated_contents(contents: &str, lints: &mut Vec) { + let mut offset = 0usize; + let mut iter = tokenize(contents).map(|t| { + let range = offset..offset + t.len; + offset = range.end; + (t.kind, &contents[range]) + }); + while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_deprecated_lint") { + let mut iter = iter + .by_ref() + .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); + let (name, reason) = match_tokens!( + iter, + // !{ + Bang OpenBrace + // #[clippy::version = "version"] + Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket + // pub LINT_NAME, + Ident Ident(name) Comma + // "description" + Literal{kind: LiteralKind::Str{..},..}(reason) + // } + CloseBrace + ); + lints.push(DeprecatedLint::new(name, reason)); + } } -/// Whether a file has had its text changed or not -#[derive(PartialEq, Debug)] -struct FileChange { - changed: bool, - new_lines: String, +/// Removes the line splices and surrounding quotes from a string literal +fn remove_line_splices(s: &str) -> String { + let s = s + .strip_prefix('r') + .unwrap_or(s) + .trim_matches('#') + .strip_prefix('"') + .and_then(|s| s.strip_suffix('"')) + .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s)); + let mut res = String::with_capacity(s.len()); + unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, _| res.push_str(&s[range])); + res } /// Replaces a region in a file delimited by two lines matching regexes. @@ -396,144 +417,49 @@ struct FileChange { /// # Panics /// /// Panics if the path could not read or then written -fn replace_region_in_file( +fn replace_region_in_file( + update_mode: UpdateMode, path: &Path, start: &str, end: &str, - replace_start: bool, - write_back: bool, - replacements: F, -) -> FileChange -where - F: FnOnce() -> Vec, -{ - let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e)); - let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements); - - if write_back { - if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) { - panic!("Cannot write to {}: {}", path.display(), e); - } - } - file_change -} - -/// Replaces a region in a text delimited by two lines matching regexes. -/// -/// * `text` is the input text on which you want to perform the replacement -/// * `start` is a `&str` that describes the delimiter line before the region you want to replace. -/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too. -/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen. -/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too. -/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end` -/// delimiter line is never replaced. -/// * `replacements` is a closure that has to return a `Vec` which contains the new text. -/// -/// If you want to perform the replacement on files instead of already parsed text, -/// use `replace_region_in_file`. -/// -/// # Example -/// -/// ```ignore -/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end"; -/// let result = -/// replace_region_in_text(the_text, "replace_start", "replace_end", false, || { -/// vec!["a different".to_string(), "text".to_string()] -/// }) -/// .new_lines; -/// assert_eq!("replace_start\na different\ntext\nreplace_end", result); -/// ``` -/// -/// # Panics -/// -/// Panics if start or end is not valid regex -fn replace_region_in_text(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange -where - F: FnOnce() -> Vec, -{ - let replace_it = replacements(); - let mut in_old_region = false; - let mut found = false; - let mut new_lines = vec![]; - let start = Regex::new(start).unwrap(); - let end = Regex::new(end).unwrap(); - - for line in text.lines() { - if in_old_region { - if end.is_match(line) { - in_old_region = false; - new_lines.extend(replace_it.clone()); - new_lines.push(line.to_string()); - } - } else if start.is_match(line) { - if !replace_start { - new_lines.push(line.to_string()); + write_replacement: impl FnMut(&mut String), +) { + let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); + let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) { + Ok(x) => x, + Err(delim) => panic!("Couldn't find `{}` in file `{}`", delim, path.display()), + }; + + match update_mode { + UpdateMode::Check if contents != new_contents => exit_with_failure(), + UpdateMode::Check => (), + UpdateMode::Change => { + if let Err(e) = fs::write(path, new_contents.as_bytes()) { + panic!("Cannot write to `{}`: {}", path.display(), e); } - in_old_region = true; - found = true; - } else { - new_lines.push(line.to_string()); - } - } - - if !found { - // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the - // given text or file. Most likely this is an error on the programmer's side and the Regex - // is incorrect. - eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start); - std::process::exit(1); - } - - let mut new_lines = new_lines.join("\n"); - if text.ends_with('\n') { - new_lines.push('\n'); + }, } - let changed = new_lines != text; - FileChange { changed, new_lines } -} - -#[test] -fn test_parse_contents() { - let result: Vec = parse_contents( - r#" -declare_clippy_lint! { - #[clippy::version = "Hello Clippy!"] - pub PTR_ARG, - style, - "really long \ - text" } -declare_clippy_lint!{ - #[clippy::version = "Test version"] - pub DOC_MARKDOWN, - pedantic, - "single line" -} - -/// some doc comment -declare_deprecated_lint! { - #[clippy::version = "I'm a version"] - pub SHOULD_ASSERT_EQ, - "`assert!()` will be more flexible with RFC 2011" -} - "#, - "module_name", - ) - .collect(); - - let expected = vec![ - Lint::new("ptr_arg", "style", "really long text", None, "module_name"), - Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"), - Lint::new( - "should_assert_eq", - "Deprecated", - "`assert!()` will be more flexible with RFC 2011", - Some("`assert!()` will be more flexible with RFC 2011"), - "module_name", - ), - ]; - assert_eq!(expected, result); +/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters +/// were found, or the missing delimiter if not. +fn replace_region_in_text<'a>( + text: &str, + start: &'a str, + end: &'a str, + mut write_replacement: impl FnMut(&mut String), +) -> Result { + let (text_start, rest) = text.split_once(start).ok_or(start)?; + let (_, text_end) = rest.split_once(end).ok_or(end)?; + + let mut res = String::with_capacity(text.len() + 4096); + res.push_str(text_start); + res.push_str(start); + write_replacement(&mut res); + res.push_str(end); + res.push_str(text_end); + + Ok(res) } #[cfg(test)] @@ -541,55 +467,65 @@ mod tests { use super::*; #[test] - fn test_replace_region() { - let text = "\nabc\n123\n789\ndef\nghi"; - let expected = FileChange { - changed: true, - new_lines: "\nabc\nhello world\ndef\nghi".to_string(), - }; - let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || { - vec!["hello world".to_string()] - }); - assert_eq!(expected, result); - } + fn test_parse_contents() { + static CONTENTS: &str = r#" + declare_clippy_lint! { + #[clippy::version = "Hello Clippy!"] + pub PTR_ARG, + style, + "really long \ + text" + } - #[test] - fn test_replace_region_with_start() { - let text = "\nabc\n123\n789\ndef\nghi"; - let expected = FileChange { - changed: true, - new_lines: "\nhello world\ndef\nghi".to_string(), - }; - let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || { - vec!["hello world".to_string()] - }); + declare_clippy_lint!{ + #[clippy::version = "Test version"] + pub DOC_MARKDOWN, + pedantic, + "single line" + } + "#; + let mut result = Vec::new(); + parse_contents(CONTENTS, "module_name", &mut result); + + let expected = vec![ + Lint::new("ptr_arg", "style", "\"really long text\"", "module_name"), + Lint::new("doc_markdown", "pedantic", "\"single line\"", "module_name"), + ]; assert_eq!(expected, result); } #[test] - fn test_replace_region_no_changes() { - let text = "123\n456\n789"; - let expected = FileChange { - changed: false, - new_lines: "123\n456\n789".to_string(), - }; - let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new); + fn test_parse_deprecated_contents() { + static DEPRECATED_CONTENTS: &str = r#" + /// some doc comment + declare_deprecated_lint! { + #[clippy::version = "I'm a version"] + pub SHOULD_ASSERT_EQ, + "`assert!()` will be more flexible with RFC 2011" + } + "#; + + let mut result = Vec::new(); + parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result); + + let expected = vec![DeprecatedLint::new( + "should_assert_eq", + "\"`assert!()` will be more flexible with RFC 2011\"", + )]; assert_eq!(expected, result); } #[test] fn test_usable_lints() { let lints = vec![ - Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"), - Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"), - Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"), - Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"), + Lint::new("should_assert_eq2", "Not Deprecated", "\"abc\"", "module_name"), + Lint::new("should_assert_eq2", "internal", "\"abc\"", "module_name"), + Lint::new("should_assert_eq2", "internal_style", "\"abc\"", "module_name"), ]; let expected = vec![Lint::new( "should_assert_eq2", "Not Deprecated", - "abc", - None, + "\"abc\"", "module_name", )]; assert_eq!(expected, Lint::usable_lints(&lints)); @@ -598,55 +534,30 @@ mod tests { #[test] fn test_by_lint_group() { let lints = vec![ - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"), - Lint::new("incorrect_match", "group1", "abc", None, "module_name"), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"), + Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name"), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"), ]; let mut expected: HashMap> = HashMap::new(); expected.insert( "group1".to_string(), vec![ - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("incorrect_match", "group1", "abc", None, "module_name"), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"), ], ); expected.insert( "group2".to_string(), - vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")], + vec![Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name")], ); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); } - #[test] - fn test_gen_changelog_lint_list() { - let lints = vec![ - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"), - ]; - let expected = vec![ - format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK), - format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK), - ]; - assert_eq!(expected, gen_changelog_lint_list(lints.iter())); - } - #[test] fn test_gen_deprecated() { let lints = vec![ - Lint::new( - "should_assert_eq", - "group1", - "abc", - Some("has been superseded by should_assert_eq2"), - "module_name", - ), - Lint::new( - "another_deprecated", - "group2", - "abc", - Some("will be removed"), - "module_name", - ), + DeprecatedLint::new("should_assert_eq", "\"has been superseded by should_assert_eq2\""), + DeprecatedLint::new("another_deprecated", "\"will be removed\""), ]; let expected = GENERATED_FILE_COMMENT.to_string() @@ -665,32 +576,15 @@ mod tests { .join("\n") + "\n"; - assert_eq!(expected, gen_deprecated(lints.iter())); - } - - #[test] - #[should_panic] - fn test_gen_deprecated_fail() { - let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")]; - let _deprecated_lints = gen_deprecated(lints.iter()); - } - - #[test] - fn test_gen_modules_list() { - let lints = vec![ - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"), - ]; - let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()]; - assert_eq!(expected, gen_modules_list(lints.iter())); + assert_eq!(expected, gen_deprecated(&lints)); } #[test] fn test_gen_lint_group_list() { let lints = vec![ - Lint::new("abc", "group1", "abc", None, "module_name"), - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("internal", "internal_style", "abc", None, "module_name"), + Lint::new("abc", "group1", "\"abc\"", "module_name"), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"), + Lint::new("internal", "internal_style", "\"abc\"", "module_name"), ]; let expected = GENERATED_FILE_COMMENT.to_string() + &[ diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 66e61660d313a..aebf9a87cabd2 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.61" +version = "0.1.62" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs new file mode 100644 index 0000000000000..e9b0f1f672de0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -0,0 +1,42 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::{meets_msrv, msrvs}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_semver::RustcVersion; + +use super::CAST_ABS_TO_UNSIGNED; + +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_expr: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, + msrv: &Option, +) { + if_chain! { + if meets_msrv(msrv.as_ref(), &msrvs::UNSIGNED_ABS); + if cast_from.is_integral(); + if cast_to.is_integral(); + if cast_from.is_signed(); + if !cast_to.is_signed(); + if let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind; + if let method_name = method_path.ident.name.as_str(); + if method_name == "abs"; + then { + span_lint_and_sugg( + cx, + CAST_ABS_TO_UNSIGNED, + expr.span, + &format!("casting the result of `{}::{}()` to {}", cast_from, method_name, cast_to), + "replace with", + format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index a4ef1344ab951..d476a1a7646c0 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_hir_ty_cfg_dependant; use clippy_utils::ty::is_c_void; -use if_chain::if_chain; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, match_any_def_paths, paths}; use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; @@ -20,45 +19,78 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } else if let ExprKind::MethodCall(method_path, [self_arg, ..], _) = &expr.kind { - if_chain! { - if method_path.ident.name == sym!(cast); - if let Some(generic_args) = method_path.args; - if let [GenericArg::Type(cast_to)] = generic_args.args; + if method_path.ident.name == sym!(cast) + && let Some(generic_args) = method_path.args + && let [GenericArg::Type(cast_to)] = generic_args.args // There probably is no obvious reason to do this, just to be consistent with `as` cases. - if !is_hir_ty_cfg_dependant(cx, cast_to); - then { - let (cast_from, cast_to) = - (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); - lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } + && !is_hir_ty_cfg_dependant(cx, cast_to) + { + let (cast_from, cast_to) = + (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } } } fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { - if_chain! { - if let ty::RawPtr(from_ptr_ty) = &cast_from.kind(); - if let ty::RawPtr(to_ptr_ty) = &cast_to.kind(); - if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty); - if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty); - if from_layout.align.abi < to_layout.align.abi; + if let ty::RawPtr(from_ptr_ty) = &cast_from.kind() + && let ty::RawPtr(to_ptr_ty) = &cast_to.kind() + && let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty) + && let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty) + && from_layout.align.abi < to_layout.align.abi // with c_void, we inherently need to trust the user - if !is_c_void(cx, from_ptr_ty.ty); + && !is_c_void(cx, from_ptr_ty.ty) // when casting from a ZST, we don't know enough to properly lint - if !from_layout.is_zst(); - then { - span_lint( - cx, - CAST_PTR_ALIGNMENT, - expr.span, - &format!( - "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", - cast_from, - cast_to, - from_layout.align.abi.bytes(), - to_layout.align.abi.bytes(), - ), - ); - } + && !from_layout.is_zst() + && !is_used_as_unaligned(cx, expr) + { + span_lint( + cx, + CAST_PTR_ALIGNMENT, + expr.span, + &format!( + "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", + cast_from, + cast_to, + from_layout.align.abi.bytes(), + to_layout.align.abi.bytes(), + ), + ); + } +} + +fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + let Some(parent) = get_parent_expr(cx, e) else { + return false; + }; + match parent.kind { + ExprKind::MethodCall(name, [self_arg, ..], _) if self_arg.hir_id == e.hir_id => { + if matches!(name.ident.as_str(), "read_unaligned" | "write_unaligned") + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) + && let Some(def_id) = cx.tcx.impl_of_method(def_id) + && cx.tcx.type_of(def_id).is_unsafe_ptr() + { + true + } else { + false + } + }, + ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => { + static PATHS: &[&[&str]] = &[ + paths::PTR_READ_UNALIGNED.as_slice(), + paths::PTR_WRITE_UNALIGNED.as_slice(), + paths::PTR_UNALIGNED_VOLATILE_LOAD.as_slice(), + paths::PTR_UNALIGNED_VOLATILE_STORE.as_slice(), + ]; + if let ExprKind::Path(path) = &func.kind + && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() + && match_any_def_paths(cx, def_id, PATHS).is_some() + { + true + } else { + false + } + }, + _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index be59145afa003..55c1f085657bb 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -1,3 +1,4 @@ +mod cast_abs_to_unsigned; mod cast_enum_constructor; mod cast_lossless; mod cast_possible_truncation; @@ -473,6 +474,28 @@ declare_clippy_lint! { "casts from an enum tuple constructor to an integer" } +declare_clippy_lint! { + /// ### What it does + /// Checks for uses of the `abs()` method that cast the result to unsigned. + /// + /// ### Why is this bad? + /// The `unsigned_abs()` method avoids panic when called on the MIN value. + /// + /// ### Example + /// ```rust + /// let x: i32 = -42; + /// let y: u32 = x.abs() as u32; + /// ``` + /// Use instead: + /// let x: i32 = -42; + /// let y: u32 = x.unsigned_abs(); + /// ``` + #[clippy::version = "1.61.0"] + pub CAST_ABS_TO_UNSIGNED, + suspicious, + "casting the result of `abs()` to an unsigned integer can panic" +} + pub struct Casts { msrv: Option, } @@ -500,7 +523,8 @@ impl_lint_pass!(Casts => [ CHAR_LIT_AS_U8, PTR_AS_PTR, CAST_ENUM_TRUNCATION, - CAST_ENUM_CONSTRUCTOR + CAST_ENUM_CONSTRUCTOR, + CAST_ABS_TO_UNSIGNED ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -536,6 +560,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); + cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); } cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 470c8c7ea26a6..af56ec11ef8ac 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -4,7 +4,8 @@ use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Lit, UnOp}; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, Lit, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; @@ -18,6 +19,17 @@ pub(super) fn check( cast_from: Ty<'_>, cast_to: Ty<'_>, ) -> bool { + // skip non-primitive type cast + if_chain! { + if let ExprKind::Cast(_, cast_to) = expr.kind; + if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind; + if let Res::PrimTy(_) = path.res; + then {} + else { + return false + } + } + if let Some(lit) = get_numeric_literal(cast_expr) { let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs new file mode 100644 index 0000000000000..fc141b4a6e3af --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs @@ -0,0 +1,125 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::{AttrKind, Attribute, Item, ItemKind}; +use rustc_ast::token::{Token, TokenKind}; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{symbol::sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for use of `crate` as opposed to `$crate` in a macro definition. + /// + /// ### Why is this bad? + /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's + /// crate. Rarely is the former intended. See: + /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene + /// + /// ### Example + /// ```rust + /// #[macro_export] + /// macro_rules! print_message { + /// () => { + /// println!("{}", crate::MESSAGE); + /// }; + /// } + /// pub const MESSAGE: &str = "Hello!"; + /// ``` + /// Use instead: + /// ```rust + /// #[macro_export] + /// macro_rules! print_message { + /// () => { + /// println!("{}", $crate::MESSAGE); + /// }; + /// } + /// pub const MESSAGE: &str = "Hello!"; + /// ``` + /// + /// Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the + /// macro definition, e.g.: + /// ```rust,ignore + /// #[allow(clippy::crate_in_macro_def)] + /// macro_rules! ok { ... crate::foo ... } + /// ``` + #[clippy::version = "1.61.0"] + pub CRATE_IN_MACRO_DEF, + suspicious, + "using `crate` in a macro definition" +} +declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); + +impl EarlyLintPass for CrateInMacroDef { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if_chain! { + if item.attrs.iter().any(is_macro_export); + if let ItemKind::MacroDef(macro_def) = &item.kind; + let tts = macro_def.body.inner_tokens(); + if let Some(span) = contains_unhygienic_crate_reference(&tts); + then { + span_lint_and_sugg( + cx, + CRATE_IN_MACRO_DEF, + span, + "`crate` references the macro call's crate", + "to reference the macro definition's crate, use", + String::from("$crate"), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn is_macro_export(attr: &Attribute) -> bool { + if_chain! { + if let AttrKind::Normal(attr_item, _) = &attr.kind; + if let [segment] = attr_item.path.segments.as_slice(); + then { + segment.ident.name == sym::macro_export + } else { + false + } + } +} + +fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { + let mut prev_is_dollar = false; + let mut cursor = tts.trees(); + while let Some(curr) = cursor.next() { + if_chain! { + if !prev_is_dollar; + if let Some(span) = is_crate_keyword(&curr); + if let Some(next) = cursor.look_ahead(0); + if is_token(next, &TokenKind::ModSep); + then { + return Some(span); + } + } + if let TokenTree::Delimited(_, _, tts) = &curr { + let span = contains_unhygienic_crate_reference(tts); + if span.is_some() { + return span; + } + } + prev_is_dollar = is_token(&curr, &TokenKind::Dollar); + } + None +} + +fn is_crate_keyword(tt: &TokenTree) -> Option { + if_chain! { + if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }) = tt; + if symbol.as_str() == "crate"; + then { Some(*span) } else { None } + } +} + +fn is_token(tt: &TokenTree, kind: &TokenKind) -> bool { + if let TokenTree::Token(Token { kind: other, .. }) = tt { + kind == other + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 92cf82bcd6a34..28d0c75fde6ba 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -621,8 +621,8 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle(false) - .expect("failed to load fallback fluent bundle"); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle"); let emitter = EmitterWriter::new( Box::new(io::sink()), None, diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index 5c4b35fd4b9d2..88c54828da834 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -1,9 +1,8 @@ -use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::ty::is_copy; -use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; +use clippy_utils::is_must_use_func_call; +use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -103,6 +102,75 @@ declare_clippy_lint! { "calls to `std::mem::forget` with a value that implements Copy" } +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `std::mem::drop` with a value that does not implement `Drop`. + /// + /// ### Why is this bad? + /// Calling `std::mem::drop` is no different than dropping such a type. A different value may + /// have been intended. + /// + /// ### Example + /// ```rust + /// struct Foo; + /// let x = Foo; + /// std::mem::drop(x); + /// ``` + #[clippy::version = "1.61.0"] + pub DROP_NON_DROP, + suspicious, + "call to `std::mem::drop` with a value which does not implement `Drop`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `std::mem::forget` with a value that does not implement `Drop`. + /// + /// ### Why is this bad? + /// Calling `std::mem::forget` is no different than dropping such a type. A different value may + /// have been intended. + /// + /// ### Example + /// ```rust + /// struct Foo; + /// let x = Foo; + /// std::mem::forget(x); + /// ``` + #[clippy::version = "1.61.0"] + pub FORGET_NON_DROP, + suspicious, + "call to `std::mem::forget` with a value which does not implement `Drop`" +} + +declare_clippy_lint! { + /// ### What it does + /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. + /// + /// ### Why is this bad? + /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`. + /// + /// ### Known problems + /// Does not catch cases if the user binds `std::mem::drop` + /// to a different name and calls it that way. + /// + /// ### Example + /// ```rust + /// struct S; + /// drop(std::mem::ManuallyDrop::new(S)); + /// ``` + /// Use instead: + /// ```rust + /// struct S; + /// unsafe { + /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); + /// } + /// ``` + #[clippy::version = "1.49.0"] + pub UNDROPPED_MANUALLY_DROPS, + correctness, + "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value" +} + const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \ Dropping a reference does nothing"; const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \ @@ -111,60 +179,65 @@ const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that imp Dropping a copy leaves the original intact"; const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \ Forgetting a copy leaves the original intact"; +const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \ + Dropping such a type only extends it's contained lifetimes"; +const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \ + Forgetting such a type is the same as dropping it"; -declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY]); +declare_lint_pass!(DropForgetRef => [ + DROP_REF, + FORGET_REF, + DROP_COPY, + FORGET_COPY, + DROP_NON_DROP, + FORGET_NON_DROP, + UNDROPPED_MANUALLY_DROPS +]); impl<'tcx> LateLintPass<'tcx> for DropForgetRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(path, args) = expr.kind; - if let ExprKind::Path(ref qpath) = path.kind; - if args.len() == 1; - if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - then { - let lint; - let msg; - let arg = &args[0]; - let arg_ty = cx.typeck_results().expr_ty(arg); - - if let ty::Ref(..) = arg_ty.kind() { - match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::mem_drop) => { - lint = DROP_REF; - msg = DROP_REF_SUMMARY.to_string(); - }, - Some(sym::mem_forget) => { - lint = FORGET_REF; - msg = FORGET_REF_SUMMARY.to_string(); - }, - _ => return, - } - span_lint_and_note(cx, - lint, - expr.span, - &msg, - Some(arg.span), - &format!("argument has type `{}`", arg_ty)); - } else if is_copy(cx, arg_ty) { - match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::mem_drop) => { - lint = DROP_COPY; - msg = DROP_COPY_SUMMARY.to_string(); - }, - Some(sym::mem_forget) => { - lint = FORGET_COPY; - msg = FORGET_COPY_SUMMARY.to_string(); - }, - _ => return, - } - span_lint_and_note(cx, - lint, - expr.span, - &msg, - Some(arg.span), - &format!("argument has type {}", arg_ty)); + if let ExprKind::Call(path, [arg]) = expr.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id) + { + let arg_ty = cx.typeck_results().expr_ty(arg); + let (lint, msg) = match fn_name { + sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY), + sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY), + sym::mem_drop if is_copy(cx, arg_ty) => (DROP_COPY, DROP_COPY_SUMMARY), + sym::mem_forget if is_copy(cx, arg_ty) => (FORGET_COPY, FORGET_COPY_SUMMARY), + sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => { + span_lint_and_help( + cx, + UNDROPPED_MANUALLY_DROPS, + expr.span, + "the inner value of this ManuallyDrop will not be dropped", + None, + "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop", + ); + return; } - } + sym::mem_drop + if !(arg_ty.needs_drop(cx.tcx, cx.param_env) + || is_must_use_func_call(cx, arg) + || is_must_use_ty(cx, arg_ty)) => + { + (DROP_NON_DROP, DROP_NON_DROP_SUMMARY) + }, + sym::mem_forget if !arg_ty.needs_drop(cx.tcx, cx.param_env) => { + (FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY) + }, + _ => return, + }; + span_lint_and_note( + cx, + lint, + expr.span, + msg, + Some(arg.span), + &format!("argument has type `{}`", arg_ty), + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs new file mode 100644 index 0000000000000..fdeac8d82557f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs @@ -0,0 +1,99 @@ +use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt}; +use rustc_ast::ast::{Item, ItemKind, VariantData}; +use rustc_errors::Applicability; +use rustc_lexer::TokenKind; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Finds structs without fields (a so-called "empty struct") that are declared with brackets. + /// + /// ### Why is this bad? + /// Empty brackets after a struct declaration can be omitted. + /// + /// ### Example + /// ```rust + /// struct Cookie {} + /// ``` + /// Use instead: + /// ```rust + /// struct Cookie; + /// ``` + #[clippy::version = "1.62.0"] + pub EMPTY_STRUCTS_WITH_BRACKETS, + restriction, + "finds struct declarations with empty brackets" +} +declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]); + +impl EarlyLintPass for EmptyStructsWithBrackets { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + let span_after_ident = item.span.with_lo(item.ident.span.hi()); + + if let ItemKind::Struct(var_data, _) = &item.kind + && has_brackets(var_data) + && has_no_fields(cx, var_data, span_after_ident) { + span_lint_and_then( + cx, + EMPTY_STRUCTS_WITH_BRACKETS, + span_after_ident, + "found empty brackets on struct declaration", + |diagnostic| { + diagnostic.span_suggestion_hidden( + span_after_ident, + "remove the brackets", + ";".to_string(), + Applicability::MachineApplicable); + }, + ); + } + } +} + +fn has_no_ident_token(braces_span_str: &str) -> bool { + !rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident) +} + +fn has_brackets(var_data: &VariantData) -> bool { + !matches!(var_data, VariantData::Unit(_)) +} + +fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool { + if !var_data.fields().is_empty() { + return false; + } + + // there might still be field declarations hidden from the AST + // (conditionaly compiled code using #[cfg(..)]) + + let Some(braces_span_str) = snippet_opt(cx, braces_span) else { + return false; + }; + + has_no_ident_token(braces_span_str.as_ref()) +} + +#[cfg(test)] +mod unit_test { + use super::*; + + #[test] + fn test_has_no_ident_token() { + let input = "{ field: u8 }"; + assert!(!has_no_ident_token(input)); + + let input = "(u8, String);"; + assert!(!has_no_ident_token(input)); + + let input = " { + // test = 5 + } + "; + assert!(has_no_ident_token(input)); + + let input = " ();"; + assert!(has_no_ident_token(input)); + } +} diff --git a/src/tools/clippy/clippy_lints/src/identity_op.rs b/src/tools/clippy/clippy_lints/src/identity_op.rs index f824f20ca40a0..4d6bef89bea7f 100644 --- a/src/tools/clippy/clippy_lints/src/identity_op.rs +++ b/src/tools/clippy/clippy_lints/src/identity_op.rs @@ -5,7 +5,7 @@ use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{clip, unsext}; @@ -54,6 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { check(cx, left, -1, e.span, right.span); check(cx, right, -1, e.span, left.span); }, + BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span), _ => (), } } @@ -70,6 +71,18 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_ && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))) } +fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { + let lhs_const = constant_full_int(cx, cx.typeck_results(), left); + let rhs_const = constant_full_int(cx, cx.typeck_results(), right); + if match (lhs_const, rhs_const) { + (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(), + (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, + _ => return, + } { + span_ineffective_operation(cx, span, arg); + } +} + fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { @@ -83,15 +96,19 @@ fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { 1 => v == 1, _ => unreachable!(), } { - span_lint( - cx, - IDENTITY_OP, - span, - &format!( - "the operation is ineffective. Consider reducing it to `{}`", - snippet(cx, arg, "..") - ), - ); + span_ineffective_operation(cx, span, arg); } } } + +fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) { + span_lint( + cx, + IDENTITY_OP, + span, + &format!( + "the operation is ineffective. Consider reducing it to `{}`", + snippet(cx, arg, "..") + ), + ); +} diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 9ead4bb27a588..4ba7477add82a 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -96,6 +96,10 @@ declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING] impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + return; + } + if let ExprKind::Index(array, index) = &expr.kind { let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::Range::hir(index) { @@ -151,6 +155,10 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { } else { // Catchall non-range index, i.e., [n] or [n << m] if let ty::Array(..) = ty.kind() { + // Index is a const block. + if let ExprKind::ConstBlock(..) = index.kind { + return; + } // Index is a constant uint. if let Some(..) = constant(cx, cx.typeck_results(), index) { // Let rustc's `const_err` lint handle constant `usize` indexing on arrays. diff --git a/src/tools/clippy/clippy_lints/src/lib.register_all.rs b/src/tools/clippy/clippy_lints/src/lib.register_all.rs index 132a466267626..14ca93b5f3c14 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_all.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_all.rs @@ -23,6 +23,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(casts::CAST_REF_TO_MUT), @@ -37,6 +38,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), LintId::of(derivable_impls::DERIVABLE_IMPLS), @@ -49,9 +51,12 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(double_comparison::DOUBLE_COMPARISONS), LintId::of(double_parens::DOUBLE_PARENS), LintId::of(drop_forget_ref::DROP_COPY), + LintId::of(drop_forget_ref::DROP_NON_DROP), LintId::of(drop_forget_ref::DROP_REF), LintId::of(drop_forget_ref::FORGET_COPY), + LintId::of(drop_forget_ref::FORGET_NON_DROP), LintId::of(drop_forget_ref::FORGET_REF), + LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), LintId::of(duration_subsec::DURATION_SUBSEC), LintId::of(entry::MAP_ENTRY), LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), @@ -152,6 +157,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::CHARS_NEXT_CMP), LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::CLONE_ON_COPY), + LintId::of(methods::ERR_EXPECT), LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::FILTER_MAP_IDENTITY), @@ -175,6 +181,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), + LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::OK_EXPECT), @@ -224,7 +231,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), - LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), @@ -296,7 +302,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(types::REDUNDANT_ALLOCATION), LintId::of(types::TYPE_COMPLEXITY), LintId::of(types::VEC_BOX), - LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_hash::UNIT_HASH), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs index a2ce69065f94d..10369a855ae6e 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs @@ -44,6 +44,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::MANUAL_SPLIT_ONCE), LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), + LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), @@ -60,7 +61,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs b/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs index df63f84463dba..6bf2c4bbaedc0 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs @@ -22,6 +22,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(drop_forget_ref::DROP_REF), LintId::of(drop_forget_ref::FORGET_COPY), LintId::of(drop_forget_ref::FORGET_REF), + LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), LintId::of(eq_op::EQ_OP), LintId::of(erasing_op::ERASING_OP), @@ -62,7 +63,6 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmuting_null::TRANSMUTING_NULL), - LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_hash::UNIT_HASH), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs index 21f1ef562b5a3..532590aaa5a3d 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs @@ -70,6 +70,7 @@ store.register_lints(&[ cargo::REDUNDANT_FEATURE_NAMES, cargo::WILDCARD_DEPENDENCIES, case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + casts::CAST_ABS_TO_UNSIGNED, casts::CAST_ENUM_CONSTRUCTOR, casts::CAST_ENUM_TRUNCATION, casts::CAST_LOSSLESS, @@ -97,6 +98,7 @@ store.register_lints(&[ copies::IF_SAME_THEN_ELSE, copies::SAME_FUNCTIONS_IN_IF_CONDITION, copy_iterator::COPY_ITERATOR, + crate_in_macro_def::CRATE_IN_MACRO_DEF, create_dir::CREATE_DIR, dbg_macro::DBG_MACRO, default::DEFAULT_TRAIT_ACCESS, @@ -122,12 +124,16 @@ store.register_lints(&[ double_comparison::DOUBLE_COMPARISONS, double_parens::DOUBLE_PARENS, drop_forget_ref::DROP_COPY, + drop_forget_ref::DROP_NON_DROP, drop_forget_ref::DROP_REF, drop_forget_ref::FORGET_COPY, + drop_forget_ref::FORGET_NON_DROP, drop_forget_ref::FORGET_REF, + drop_forget_ref::UNDROPPED_MANUALLY_DROPS, duration_subsec::DURATION_SUBSEC, else_if_without_else::ELSE_IF_WITHOUT_ELSE, empty_enum::EMPTY_ENUM, + empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS, entry::MAP_ENTRY, enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, enum_variants::ENUM_VARIANT_NAMES, @@ -280,6 +286,7 @@ store.register_lints(&[ methods::CLONE_DOUBLE_REF, methods::CLONE_ON_COPY, methods::CLONE_ON_REF_PTR, + methods::ERR_EXPECT, methods::EXPECT_FUN_CALL, methods::EXPECT_USED, methods::EXTEND_WITH_DRAIN, @@ -313,6 +320,7 @@ store.register_lints(&[ methods::MAP_FLATTEN, methods::MAP_IDENTITY, methods::MAP_UNWRAP_OR, + methods::NEEDLESS_OPTION_AS_DEREF, methods::NEEDLESS_SPLITN, methods::NEW_RET_NO_SELF, methods::OK_EXPECT, @@ -384,7 +392,6 @@ store.register_lints(&[ needless_continue::NEEDLESS_CONTINUE, needless_for_each::NEEDLESS_FOR_EACH, needless_late_init::NEEDLESS_LATE_INIT, - needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF, needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, needless_question_mark::NEEDLESS_QUESTION_MARK, needless_update::NEEDLESS_UPDATE, @@ -505,7 +512,6 @@ store.register_lints(&[ types::TYPE_COMPLEXITY, types::VEC_BOX, undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS, - undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, unicode::INVISIBLE_CHARACTERS, unicode::NON_ASCII_LITERAL, unicode::UNICODE_NOT_NFC, diff --git a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs index 6ab139b2fb67b..4802dd877e99d 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs @@ -16,6 +16,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION), LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS), LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), LintId::of(exit::EXIT), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_style.rs b/src/tools/clippy/clippy_lints/src/lib.register_style.rs index dcf399cf9562f..3114afac8863e 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_style.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_style.rs @@ -59,6 +59,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(methods::BYTES_NTH), LintId::of(methods::CHARS_LAST_CMP), LintId::of(methods::CHARS_NEXT_CMP), + LintId::of(methods::ERR_EXPECT), LintId::of(methods::INTO_ITER_ON_REF), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_NEXT_SLICE), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs index fa3a88e1368ce..82f45b5fd58b9 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs @@ -7,8 +7,12 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), + LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), + LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), + LintId::of(drop_forget_ref::DROP_NON_DROP), + LintId::of(drop_forget_ref::FORGET_NON_DROP), LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index f2a0799914448..c9b836f95808a 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -1,5 +1,6 @@ // error-pattern:cargo-clippy +#![feature(array_windows)] #![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] #![feature(control_flow_enum)] @@ -190,6 +191,7 @@ mod collapsible_match; mod comparison_chain; mod copies; mod copy_iterator; +mod crate_in_macro_def; mod create_dir; mod dbg_macro; mod default; @@ -208,6 +210,7 @@ mod drop_forget_ref; mod duration_subsec; mod else_if_without_else; mod empty_enum; +mod empty_structs_with_brackets; mod entry; mod enum_clike; mod enum_variants; @@ -305,7 +308,6 @@ mod needless_borrowed_ref; mod needless_continue; mod needless_for_each; mod needless_late_init; -mod needless_option_as_deref; mod needless_pass_by_value; mod needless_question_mark; mod needless_update; @@ -375,7 +377,6 @@ mod transmuting_null; mod try_err; mod types; mod undocumented_unsafe_blocks; -mod undropped_manually_drops; mod unicode; mod uninit_vec; mod unit_hash; @@ -533,7 +534,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(ptr::Ptr)); store.register_late_pass(|| Box::new(ptr_eq::PtrEq)); store.register_late_pass(|| Box::new(needless_bool::NeedlessBool)); - store.register_late_pass(|| Box::new(needless_option_as_deref::OptionNeedlessDeref)); store.register_late_pass(|| Box::new(needless_bool::BoolComparison)); store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach)); store.register_late_pass(|| Box::new(misc::MiscLints)); @@ -812,7 +812,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); - store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops)); store.register_late_pass(|| Box::new(strings::StrToString)); store.register_late_pass(|| Box::new(strings::StringToString)); store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues)); @@ -847,7 +846,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: enable_raw_pointer_heuristic_for_send, )) }); - store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default())); + store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch)); store.register_late_pass(move || Box::new(format_args::FormatArgs)); store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray)); @@ -867,6 +866,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ignore_publish: cargo_ignore_publish, }) }); + store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); + store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs index 36ecd83f7d643..a0bd7ad0ac647 100644 --- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs @@ -2,9 +2,12 @@ use super::SINGLE_ELEMENT_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet_with_applicability}; use if_chain::if_chain; +use rustc_ast::util::parser::PREC_PREFIX; +use rustc_ast::Mutability; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, Pat}; +use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat}; use rustc_lint::LateContext; +use rustc_span::edition::Edition; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -13,31 +16,84 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { - let arg_expr = match arg.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg, - ExprKind::MethodCall(method, [arg], _) if method.ident.name == rustc_span::sym::iter => arg, + let (arg_expression, prefix) = match arg.kind { + ExprKind::AddrOf( + BorrowKind::Ref, + Mutability::Not, + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ) => (arg, "&"), + ExprKind::AddrOf( + BorrowKind::Ref, + Mutability::Mut, + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ) => (arg, "&mut "), + ExprKind::MethodCall( + method, + [ + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ], + _, + ) if method.ident.name == rustc_span::sym::iter => (arg, "&"), + ExprKind::MethodCall( + method, + [ + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ], + _, + ) if method.ident.name.as_str() == "iter_mut" => (arg, "&mut "), + ExprKind::MethodCall( + method, + [ + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ], + _, + ) if method.ident.name == rustc_span::sym::into_iter => (arg, ""), + // Only check for arrays edition 2021 or later, as this case will trigger a compiler error otherwise. + ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""), _ => return, }; if_chain! { - if let ExprKind::Array([arg_expression]) = arg_expr.kind; if let ExprKind::Block(block, _) = body.kind; if !block.stmts.is_empty(); then { let mut applicability = Applicability::MachineApplicable; let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); - let arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); + let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned(); block_str.remove(0); block_str.pop(); let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)); + // Reference iterator from `&(mut) []` or `[].iter(_mut)()`. + if !prefix.is_empty() && ( + // Precedence of internal expression is less than or equal to precedence of `&expr`. + arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression) + ) { + arg_snip = format!("({arg_snip})").into(); + } + span_lint_and_sugg( cx, SINGLE_ELEMENT_LOOP, expr.span, "for loop over a single element", "try", - format!("{{\n{}let {} = &{};{}}}", indent, pat_snip, arg_snip, block_str), + format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"), applicability, ) } diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index 0f6ac47843241..f552d5c1afab9 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{iter_input_pats, method_chain_args}; use if_chain::if_chain; @@ -217,36 +217,33 @@ fn lint_map_unit_fn(cx: &LateContext<'_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr let fn_arg = &map_args[1]; if is_unit_function(cx, fn_arg) { + let mut applicability = Applicability::MachineApplicable; let msg = suggestion_msg("function", map_type); let suggestion = format!( "if let {0}({binding}) = {1} {{ {2}({binding}) }}", variant, - snippet(cx, var_arg.span, "_"), - snippet(cx, fn_arg.span, "_"), + snippet_with_applicability(cx, var_arg.span, "_", &mut applicability), + snippet_with_applicability(cx, fn_arg.span, "_", &mut applicability), binding = let_binding_name(cx, var_arg) ); span_lint_and_then(cx, lint, expr.span, &msg, |diag| { - diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::MachineApplicable); + diag.span_suggestion(stmt.span, "try this", suggestion, applicability); }); } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) { let msg = suggestion_msg("closure", map_type); span_lint_and_then(cx, lint, expr.span, &msg, |diag| { if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) { + let mut applicability = Applicability::MachineApplicable; let suggestion = format!( "if let {0}({1}) = {2} {{ {3} }}", variant, - snippet(cx, binding.pat.span, "_"), - snippet(cx, var_arg.span, "_"), - snippet(cx, reduced_expr_span, "_") - ); - diag.span_suggestion( - stmt.span, - "try this", - suggestion, - Applicability::MachineApplicable, // snippet + snippet_with_applicability(cx, binding.pat.span, "_", &mut applicability), + snippet_with_applicability(cx, var_arg.span, "_", &mut applicability), + snippet_with_context(cx, reduced_expr_span, var_arg.span.ctxt(), "_", &mut applicability).0, ); + diag.span_suggestion(stmt.span, "try this", suggestion, applicability); } else { let suggestion = format!( "if let {0}({1}) = {2} {{ ... }}", diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index ff85623acf49b..e93b494653fc0 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -667,7 +667,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { overlapping_arms::check(cx, ex, arms); match_wild_enum::check(cx, ex, arms); match_as_ref::check(cx, ex, arms, expr); - needless_match::check_match(cx, ex, arms); + needless_match::check_match(cx, ex, arms, expr); if self.infallible_destructuring_match_linted { self.infallible_destructuring_match_linted = false; diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 76131d307d777..2105a03e03a30 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -1,37 +1,25 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, get_parent_expr, higher, is_else_clause, is_lang_ctor, peel_blocks_with_stmt}; +use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::{ + eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, over, + peel_blocks_with_stmt, +}; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, Pat, PatKind, Path, PathSegment, QPath, UnOp}; +use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::sym; +use rustc_typeck::hir_ty_to_ty; -pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { - // This is for avoiding collision with `match_single_binding`. - if arms.len() < 2 { - return; - } - - for arm in arms { - if let PatKind::Wild = arm.pat.kind { - let ret_expr = strip_return(arm.body); - if !eq_expr_value(cx, ex, ret_expr) { - return; - } - } else if !pat_same_as_expr(arm.pat, arm.body) { - return; - } - } - - if let Some(match_expr) = get_parent_expr(cx, ex) { +pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { + if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, NEEDLESS_MATCH, - match_expr.span, + expr.span, "this match expression is unnecessary", "replace it with", snippet_with_applicability(cx, ex.span, "..", &mut applicability).to_string(), @@ -60,11 +48,8 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) /// } /// ``` pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) { - if_chain! { - if let Some(ref if_let) = higher::IfLet::hir(cx, ex); - if !is_else_clause(cx.tcx, ex); - if check_if_let(cx, if_let); - then { + if let Some(ref if_let) = higher::IfLet::hir(cx, ex) { + if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -79,6 +64,19 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) { } } +fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool { + for arm in arms { + let arm_expr = peel_blocks_with_stmt(arm.body); + if let PatKind::Wild = arm.pat.kind { + return eq_expr_value(cx, match_expr, strip_return(arm_expr)); + } else if !pat_same_as_expr(arm.pat, arm_expr) { + return false; + } + } + + true +} + fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if let Some(if_else) = if_let.if_else { if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) { @@ -92,18 +90,21 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if matches!(if_else.kind, ExprKind::Block(..)) { let else_expr = peel_blocks_with_stmt(if_else); + if matches!(else_expr.kind, ExprKind::Block(..)) { + return false; + } let ret = strip_return(else_expr); let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { if let ExprKind::Path(ref qpath) = ret.kind { return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); } - } else { - return eq_expr_value(cx, if_let.let_expr, ret); + return true; } - return true; + return eq_expr_value(cx, if_let.let_expr, ret); } } + false } @@ -116,48 +117,70 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { } } +/// Manually check for coercion casting by checking if the type of the match operand or let expr +/// differs with the assigned local variable or the funtion return type. +fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { + if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) { + match p_node { + // Compare match_expr ty with local in `let local = match match_expr {..}` + Node::Local(local) => { + let results = cx.typeck_results(); + return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); + }, + // compare match_expr ty with RetTy in `fn foo() -> RetTy` + Node::Item(..) => { + if let Some(fn_decl) = p_node.fn_decl() { + if let FnRetTy::Return(ret_ty) = fn_decl.output { + return same_type_and_consts(hir_ty_to_ty(cx.tcx, ret_ty), cx.typeck_results().expr_ty(expr)); + } + } + }, + // check the parent expr for this whole block `{ match match_expr {..} }` + Node::Block(block) => { + if let Some(block_parent_expr) = get_parent_expr_for_hir(cx, block.hir_id) { + return expr_ty_matches_p_ty(cx, expr, block_parent_expr); + } + }, + // recursively call on `if xxx {..}` etc. + Node::Expr(p_expr) => { + return expr_ty_matches_p_ty(cx, expr, p_expr); + }, + _ => {}, + } + } + false +} + fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { let expr = strip_return(expr); match (&pat.kind, &expr.kind) { // Example: `Some(val) => Some(val)` - ( - PatKind::TupleStruct(QPath::Resolved(_, path), [first_pat, ..], _), - ExprKind::Call(call_expr, [first_param, ..]), - ) => { + (PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => { if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind { - if has_identical_segments(path.segments, call_path.segments) - && has_same_non_ref_symbol(first_pat, first_param) - { - return true; - } + return over(path.segments, call_path.segments, |pat_seg, call_seg| { + pat_seg.ident.name == call_seg.ident.name + }) && same_non_ref_symbols(tuple_params, call_params); } }, - // Example: `val => val`, or `ref val => *val` - (PatKind::Binding(annot, _, pat_ident, _), _) => { - let new_expr = if let ( - BindingAnnotation::Ref | BindingAnnotation::RefMut, - ExprKind::Unary(UnOp::Deref, operand_expr), - ) = (annot, &expr.kind) - { - operand_expr - } else { - expr - }; - - if let ExprKind::Path(QPath::Resolved( + // Example: `val => val` + ( + PatKind::Binding(annot, _, pat_ident, _), + ExprKind::Path(QPath::Resolved( _, Path { segments: [first_seg, ..], .. }, - )) = new_expr.kind - { - return pat_ident.name == first_seg.ident.name; - } + )), + ) => { + return !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut) + && pat_ident.name == first_seg.ident.name; }, // Example: `Custom::TypeA => Custom::TypeB`, or `None => None` (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => { - return has_identical_segments(p_path.segments, e_path.segments); + return over(p_path.segments, e_path.segments, |p_seg, e_seg| { + p_seg.ident.name == e_seg.ident.name + }); }, // Example: `5 => 5` (PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => { @@ -171,27 +194,16 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { false } -fn has_identical_segments(left_segs: &[PathSegment<'_>], right_segs: &[PathSegment<'_>]) -> bool { - if left_segs.len() != right_segs.len() { +fn same_non_ref_symbols(pats: &[Pat<'_>], exprs: &[Expr<'_>]) -> bool { + if pats.len() != exprs.len() { return false; } - for i in 0..left_segs.len() { - if left_segs[i].ident.name != right_segs[i].ident.name { - return false; - } - } - true -} -fn has_same_non_ref_symbol(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { - if_chain! { - if let PatKind::Binding(annot, _, pat_ident, _) = pat.kind; - if !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); - if let ExprKind::Path(QPath::Resolved(_, Path {segments: [first_seg, ..], .. })) = expr.kind; - then { - return pat_ident.name == first_seg.ident.name; + for i in 0..pats.len() { + if !pat_same_as_expr(&pats[i], &exprs[i]) { + return false; } } - false + true } diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs index 76eaedea8a0d2..44857d61fef8f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E cx, BYTES_NTH, expr.span, - &format!("called `.byte().nth()` on a `{}`", caller_type), + &format!("called `.bytes().nth()` on a `{}`", caller_type), "try", format!( "{}.as_bytes().get({})", diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs new file mode 100644 index 0000000000000..be9d4ad94fb8e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs @@ -0,0 +1,60 @@ +use super::ERR_EXPECT; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item}; +use rustc_errors::Applicability; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_middle::ty::Ty; +use rustc_semver::RustcVersion; +use rustc_span::{sym, Span}; + +pub(super) fn check( + cx: &LateContext<'_>, + _expr: &rustc_hir::Expr<'_>, + recv: &rustc_hir::Expr<'_>, + msrv: Option<&RustcVersion>, + expect_span: Span, + err_span: Span, +) { + if_chain! { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + // Test the version to make sure the lint can be showed (expect_err has been + // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) + if meets_msrv(msrv, &msrvs::EXPECT_ERR); + + // Grabs the `Result` type + let result_type = cx.typeck_results().expr_ty(recv); + // Tests if the T type in a `Result` is not None + if let Some(data_type) = get_data_type(cx, result_type); + // Tests if the T type in a `Result` implements debug + if has_debug_impl(data_type, cx); + + then { + span_lint_and_sugg( + cx, + ERR_EXPECT, + err_span.to(expect_span), + "called `.err().expect()` on a `Result` value", + "try", + "expect_err".to_string(), + Applicability::MachineApplicable + ); + } + }; +} + +/// Given a `Result` type, return its data (`T`). +fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { + match ty.kind() { + ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::Result) => substs.types().next(), + _ => None, + } +} + +/// Given a type, very if the Debug trait has been impl'd +fn has_debug_impl<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { + cx.tcx + .get_diagnostic_item(sym::Debug) + .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) +} diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs index c98cdfbca434e..9651a52be4e72 100644 --- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs @@ -48,13 +48,11 @@ pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr), "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned), "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path), - "to_vec" => { - cx.tcx.impl_of_method(method_def_id) - .filter(|&impl_did| { - cx.tcx.type_of(impl_did).is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none() - }) - .is_some() - }, + "to_vec" => cx + .tcx + .impl_of_method(method_def_id) + .filter(|&impl_did| cx.tcx.type_of(impl_did).is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none()) + .is_some(), _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs index b93f1399eaeed..54c9ca435a447 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::{get_iterator_item_ty, is_copy}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy}; use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; +use rustc_span::sym; use std::ops::Not; use super::ITER_OVEREAGER_CLONED; @@ -20,9 +21,16 @@ pub(super) fn check<'tcx>( map_arg: &[hir::Expr<'_>], ) { // Check if it's iterator and get type associated with `Item`. - let inner_ty = match get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv)) { - Some(ty) => ty, - _ => return, + let inner_ty = if_chain! { + if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + let recv_ty = cx.typeck_results().expr_ty(recv); + if implements_trait(cx, recv_ty, iterator_trait_id, &[]); + if let Some(inner_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv)); + then { + inner_ty + } else { + return; + } }; match inner_ty.kind() { diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs index f112b500d3d29..862a9578e6ff2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -13,6 +13,7 @@ pub(super) fn check( expr: &hir::Expr<'_>, caller: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, + name: &str, _map_span: Span, ) { let caller_ty = cx.typeck_results().expr_ty(caller); @@ -29,7 +30,7 @@ pub(super) fn check( MAP_IDENTITY, sugg_span, "unnecessary map of the identity function", - "remove the call to `map`", + &format!("remove the call to `{}`", name), String::new(), Applicability::MachineApplicable, ) diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 9d4e1fa399401..70d021a1668eb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -9,6 +9,7 @@ mod chars_next_cmp_with_unwrap; mod clone_on_copy; mod clone_on_ref_ptr; mod cloned_instead_of_copied; +mod err_expect; mod expect_fun_call; mod expect_used; mod extend_with_drain; @@ -40,6 +41,7 @@ mod map_collect_result_unit; mod map_flatten; mod map_identity; mod map_unwrap_or; +mod needless_option_as_deref; mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; @@ -362,6 +364,29 @@ declare_clippy_lint! { "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `.err().expect()` calls on the `Result` type. + /// + /// ### Why is this bad? + /// `.expect_err()` can be called directly to avoid the extra type conversion from `err()`. + /// + /// ### Example + /// ```should_panic + /// let x: Result = Ok(10); + /// x.err().expect("Testing err().expect()"); + /// ``` + /// Use instead: + /// ```should_panic + /// let x: Result = Ok(10); + /// x.expect_err("Testing expect_err"); + /// ``` + #[clippy::version = "1.61.0"] + pub ERR_EXPECT, + style, + r#"using `.err().expect("")` when `.expect_err("")` can be used"# +} + declare_clippy_lint! { /// ### What it does /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and @@ -2055,7 +2080,7 @@ declare_clippy_lint! { /// Checks for use of `.collect::>().join("")` on iterators. /// /// ### Why is this bad? - /// `.collect::()` is more concise and usually more performant + /// `.collect::()` is more concise and might be more performant /// /// ### Example /// ```rust @@ -2070,15 +2095,42 @@ declare_clippy_lint! { /// println!("{}", output); /// ``` /// ### Known problems - /// While `.collect::()` is more performant in most cases, there are cases where + /// While `.collect::()` is sometimes more performant, there are cases where /// using `.collect::()` over `.collect::>().join("")` /// will prevent loop unrolling and will result in a negative performance impact. + /// + /// Additionlly, differences have been observed between aarch64 and x86_64 assembly output, + /// with aarch64 tending to producing faster assembly in more cases when using `.collect::()` #[clippy::version = "1.61.0"] pub UNNECESSARY_JOIN, pedantic, "using `.collect::>().join(\"\")` on an iterator" } +declare_clippy_lint! { + /// ### What it does + /// Checks for no-op uses of `Option::{as_deref, as_deref_mut}`, + /// for example, `Option<&T>::as_deref()` returns the same type. + /// + /// ### Why is this bad? + /// Redundant code and improving readability. + /// + /// ### Example + /// ```rust + /// let a = Some(&1); + /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32> + /// ``` + /// Could be written as: + /// ```rust + /// let a = Some(&1); + /// let b = a; + /// ``` + #[clippy::version = "1.57.0"] + pub NEEDLESS_OPTION_AS_DEREF, + complexity, + "no-op use of `deref` or `deref_mut` method to `Option`." +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2165,6 +2217,8 @@ impl_lint_pass!(Methods => [ NEEDLESS_SPLITN, UNNECESSARY_TO_OWNED, UNNECESSARY_JOIN, + ERR_EXPECT, + NEEDLESS_OPTION_AS_DEREF, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2397,6 +2451,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } }, + ("as_deref" | "as_deref_mut", []) => { + needless_option_as_deref::check(cx, expr, recv, name); + }, ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), @@ -2428,6 +2485,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("expect", [_]) => match method_call(recv) { Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), + Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, msrv, span, err_span), _ => expect_used::check(cx, expr, recv), }, ("extend", [arg]) => { @@ -2472,7 +2530,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } } }, - ("map", [m_arg]) => { + (name @ ("map" | "map_err"), [m_arg]) => { if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) { match (name, args) { ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv), @@ -2484,7 +2542,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio _ => {}, } } - map_identity::check(cx, expr, recv, m_arg, span); + map_identity::check(cx, expr, recv, m_arg, name, span); }, ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), (name @ "next", args @ []) => { diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs new file mode 100644 index 0000000000000..7030baf19ff5c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs @@ -0,0 +1,37 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::path_res; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::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; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::NEEDLESS_OPTION_AS_DEREF; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name: &str) { + let typeck = cx.typeck_results(); + let outer_ty = typeck.expr_ty(expr); + + if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) { + if name == "as_deref_mut" && recv.is_syntactic_place_expr() { + let Res::Local(binding_id) = path_res(cx, recv) else { return }; + + if local_used_after_expr(cx, binding_id, recv) { + return; + } + } + + span_lint_and_sugg( + cx, + NEEDLESS_OPTION_AS_DEREF, + expr.span, + "derefed type is same as origin", + "try this", + snippet_opt(cx, recv.span).unwrap(), + Applicability::MachineApplicable, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index b8dfe99688066..0a393657267b0 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -1,17 +1,14 @@ -use std::{ - ffi::OsString, - path::{Component, Path}, -}; - use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext}; +use std::ffi::OsStr; +use std::path::{Component, Path}; declare_clippy_lint! { /// ### What it does - /// Checks that module layout uses only self named module files, bans mod.rs files. + /// Checks that module layout uses only self named module files, bans `mod.rs` files. /// /// ### Why is this bad? /// Having multiple module layout styles in a project can be confusing. @@ -40,7 +37,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks that module layout uses only mod.rs files. + /// Checks that module layout uses only `mod.rs` files. /// /// ### Why is this bad? /// Having multiple module layout styles in a project can be confusing. @@ -82,11 +79,7 @@ impl EarlyLintPass for ModStyle { let files = cx.sess().source_map().files(); - let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess().opts.working_dir { - p.to_string_lossy() - } else { - return; - }; + let RealFileName::LocalPath(trim_to_src) = &cx.sess().opts.working_dir else { return }; // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives // `[path, to]` but not foo @@ -97,26 +90,27 @@ impl EarlyLintPass for ModStyle { // `{ foo => path/to/foo.rs, .. } let mut file_map = FxHashMap::default(); for file in files.iter() { - match &file.name { - FileName::Real(RealFileName::LocalPath(lp)) - if lp.to_string_lossy().starts_with(trim_to_src.as_ref()) => - { - let p = lp.to_string_lossy(); - let path = Path::new(p.trim_start_matches(trim_to_src.as_ref())); - if let Some(stem) = path.file_stem() { - file_map.insert(stem.to_os_string(), (file, path.to_owned())); - } - process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders); - check_self_named_mod_exists(cx, path, file); - }, - _ => {}, + if let FileName::Real(RealFileName::LocalPath(lp)) = &file.name { + let path = if lp.is_relative() { + lp + } else if let Ok(relative) = lp.strip_prefix(trim_to_src) { + relative + } else { + continue; + }; + + if let Some(stem) = path.file_stem() { + file_map.insert(stem, (file, path)); + } + process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders); + check_self_named_mod_exists(cx, path, file); } } for folder in &folder_segments { if !mod_folders.contains(folder) { if let Some((file, path)) = file_map.get(folder) { - let mut correct = path.clone(); + let mut correct = path.to_path_buf(); correct.pop(); correct.push(folder); correct.push("mod.rs"); @@ -138,25 +132,17 @@ impl EarlyLintPass for ModStyle { /// For each `path` we add each folder component to `folder_segments` and if the file name /// is `mod.rs` we add it's parent folder to `mod_folders`. -fn process_paths_for_mod_files( - path: &Path, - folder_segments: &mut FxHashSet, - mod_folders: &mut FxHashSet, +fn process_paths_for_mod_files<'a>( + path: &'a Path, + folder_segments: &mut FxHashSet<&'a OsStr>, + mod_folders: &mut FxHashSet<&'a OsStr>, ) { let mut comp = path.components().rev().peekable(); let _ = comp.next(); if path.ends_with("mod.rs") { - mod_folders.insert(comp.peek().map(|c| c.as_os_str().to_owned()).unwrap_or_default()); + mod_folders.insert(comp.peek().map(|c| c.as_os_str()).unwrap_or_default()); } - let folders = comp - .filter_map(|c| { - if let Component::Normal(s) = c { - Some(s.to_os_string()) - } else { - None - } - }) - .collect::>(); + let folders = comp.filter_map(|c| if let Component::Normal(s) = c { Some(s) } else { None }); folder_segments.extend(folders); } diff --git a/src/tools/clippy/clippy_lints/src/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/needless_option_as_deref.rs deleted file mode 100644 index 9d3d7d1f24cbc..0000000000000 --- a/src/tools/clippy/clippy_lints/src/needless_option_as_deref.rs +++ /dev/null @@ -1,65 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; -use clippy_utils::ty::is_type_diagnostic_item; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for no-op uses of Option::{as_deref,as_deref_mut}, - /// for example, `Option<&T>::as_deref()` returns the same type. - /// - /// ### Why is this bad? - /// Redundant code and improving readability. - /// - /// ### Example - /// ```rust - /// let a = Some(&1); - /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32> - /// ``` - /// Could be written as: - /// ```rust - /// let a = Some(&1); - /// let b = a; - /// ``` - #[clippy::version = "1.57.0"] - pub NEEDLESS_OPTION_AS_DEREF, - complexity, - "no-op use of `deref` or `deref_mut` method to `Option`." -} - -declare_lint_pass!(OptionNeedlessDeref=> [ - NEEDLESS_OPTION_AS_DEREF, -]); - -impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { - return; - } - let typeck = cx.typeck_results(); - let outer_ty = typeck.expr_ty(expr); - - if_chain! { - if is_type_diagnostic_item(cx,outer_ty,sym::Option); - if let ExprKind::MethodCall(path, [sub_expr], _) = expr.kind; - let symbol = path.ident.as_str(); - if symbol == "as_deref" || symbol == "as_deref_mut"; - if outer_ty == typeck.expr_ty(sub_expr); - then{ - span_lint_and_sugg( - cx, - NEEDLESS_OPTION_AS_DEREF, - expr.span, - "derefed type is same as origin", - "try this", - snippet_opt(cx,sub_expr.span).unwrap(), - Applicability::MachineApplicable - ); - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index 6ef6b9a20aa4b..2f3007658ea62 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -78,6 +78,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; if is_panic(cx, macro_call.def_id) { + if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + return; + } + span_lint( cx, PANIC, diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 5f453dc165558..48a2666a2e0ce 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -601,9 +601,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: }, // If the types match check for methods which exist on both types. e.g. `Vec::len` and // `slice::len` - ty::Adt(def, _) - if def.did() == args.ty_did => - { + ty::Adt(def, _) if def.did() == args.ty_did => { set_skip_flag(); }, _ => (), diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index 02569bd3a476e..342f23f030cd0 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -410,9 +410,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if cx.tcx.is_diagnostic_item(sym::transmute, def_id); then { - // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts. - // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. - // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers. + // Avoid suggesting non-const operations in const contexts: + // - from/to bits (https://github.com/rust-lang/rust/issues/73736) + // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911) + // - char conversions (https://github.com/rust-lang/rust/issues/89259) let const_context = in_constant(cx, e.hir_id); let from_ty = cx.typeck_results().expr_ty_adjusted(arg); @@ -427,7 +428,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath) - | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg) + | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs index 3eb07b68992a8..9e1823c373bfd 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -15,9 +15,10 @@ pub(super) fn check<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, + const_context: bool, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) => { + (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) if !const_context => { span_lint_and_then( cx, TRANSMUTE_INT_TO_CHAR, diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 7570bc2a7a8f0..786e7bfc56f6e 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -32,18 +32,20 @@ pub(super) fn check<'tcx>( "" }; + let snippet = snippet(cx, arg.span, ".."); + span_lint_and_sugg( cx, TRANSMUTE_BYTES_TO_STR, e.span, &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), "consider using", - format!( - "std::str::from_utf8{}({}).unwrap()", - postfix, - snippet(cx, arg.span, ".."), - ), - Applicability::Unspecified, + if const_context { + format!("std::str::from_utf8_unchecked{postfix}({snippet})") + } else { + format!("std::str::from_utf8{postfix}({snippet}).unwrap()") + }, + Applicability::MaybeIncorrect, ); triggered = true; } else { diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index e42c6c63ede0b..c8912a18f1854 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,16 +1,13 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_lint_allowed; -use clippy_utils::source::{indent_of, reindent_multiline, snippet}; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, HirId, Local, UnsafeSource}; -use rustc_lexer::TokenKind; -use rustc_lint::{LateContext, LateLintPass}; +use clippy_utils::source::walk_span_to_context; +use rustc_data_structures::sync::Lrc; +use rustc_hir::{Block, BlockCheckMode, UnsafeSource}; +use rustc_lexer::{tokenize, TokenKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::TyCtxt; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{BytePos, Span}; -use std::borrow::Cow; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Pos, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -18,6 +15,24 @@ declare_clippy_lint! { /// explaining why the unsafe operations performed inside /// the block are safe. /// + /// Note the comment must appear on the line(s) preceding the unsafe block + /// with nothing appearing in between. The following is ok: + /// ```ignore + /// foo( + /// // SAFETY: + /// // This is a valid safety comment + /// unsafe { *x } + /// ) + /// ``` + /// But neither of these are: + /// ```ignore + /// // SAFETY: + /// // This is not a valid safety comment + /// foo( + /// /* SAFETY: Neither is this */ unsafe { *x }, + /// ); + /// ``` + /// /// ### Why is this bad? /// Undocumented unsafe blocks can make it difficult to /// read and maintain code, as well as uncover unsoundness @@ -44,179 +59,139 @@ declare_clippy_lint! { "creating an unsafe block without explaining why it is safe" } -impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]); - -#[derive(Default)] -pub struct UndocumentedUnsafeBlocks { - pub local_level: u32, - pub local_span: Option, - // The local was already checked for an overall safety comment - // There is no need to continue checking the blocks in the local - pub local_checked: bool, - // Since we can only check the blocks from expanded macros - // We have to omit the suggestion due to the actual definition - // Not being available to us - pub macro_expansion: bool, -} +declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]); impl LateLintPass<'_> for UndocumentedUnsafeBlocks { fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) { - if_chain! { - if !self.local_checked; - if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id); - if !in_external_macro(cx.tcx.sess, block.span); - if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules; - if let Some(enclosing_scope_hir_id) = cx.tcx.hir().get_enclosing_scope(block.hir_id); - if self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, block.span) == Some(false); - then { - let mut span = block.span; - - if let Some(local_span) = self.local_span { - span = local_span; - - let result = self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, span); + if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + && !in_external_macro(cx.tcx.sess, block.span) + && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) + && !is_unsafe_from_proc_macro(cx, block) + && !block_has_safety_comment(cx, block) + { + let source_map = cx.tcx.sess.source_map(); + let span = if source_map.is_multiline(block.span) { + source_map.span_until_char(block.span, '\n') + } else { + block.span + }; - if result.unwrap_or(true) { - self.local_checked = true; - return; - } - } - - self.lint(cx, span); - } - } - } - - fn check_local(&mut self, cx: &LateContext<'_>, local: &'_ Local<'_>) { - if_chain! { - if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, local.hir_id); - if !in_external_macro(cx.tcx.sess, local.span); - if let Some(init) = local.init; - then { - self.visit_expr(init); - - if self.local_level > 0 { - self.local_span = Some(local.span); - } - } + span_lint_and_help( + cx, + UNDOCUMENTED_UNSAFE_BLOCKS, + span, + "unsafe block missing a safety comment", + None, + "consider adding a safety comment on the preceding line", + ); } } +} - fn check_block_post(&mut self, _: &LateContext<'_>, _: &'_ Block<'_>) { - self.local_level = self.local_level.saturating_sub(1); - - if self.local_level == 0 { - self.local_checked = false; - self.local_span = None; - } - } +fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { + let source_map = cx.sess().source_map(); + let file_pos = source_map.lookup_byte_offset(block.span.lo()); + file_pos + .sf + .src + .as_deref() + .and_then(|src| src.get(file_pos.pos.to_usize()..)) + .map_or(true, |src| !src.starts_with("unsafe")) } -impl<'v> Visitor<'v> for UndocumentedUnsafeBlocks { - fn visit_expr(&mut self, ex: &'v Expr<'v>) { - match ex.kind { - ExprKind::Block(_, _) => self.local_level = self.local_level.saturating_add(1), - _ => walk_expr(self, ex), +/// Checks if the lines immediately preceding the block contain a safety comment. +fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { + // This intentionally ignores text before the start of a function so something like: + // ``` + // // SAFETY: reason + // fn foo() { unsafe { .. } } + // ``` + // won't work. This is to avoid dealing with where such a comment should be place relative to + // attributes and doc comments. + + let source_map = cx.sess().source_map(); + let ctxt = block.span.ctxt(); + if ctxt != SyntaxContext::root() { + // From a macro expansion. Get the text from the start of the macro declaration to start of the unsafe block. + // macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; } + // ^--------------------------------------------^ + if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo()) + && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo()) + && Lrc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + macro_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines[macro_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + } else { + // Problem getting source text. Pretend a comment was found. + true } + } else if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo()) + && let Some(body) = cx.enclosing_body + && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root()) + && let Ok(body_line) = source_map.lookup_line(body_span.lo()) + && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + // Get the text from the start of function body to the unsafe block. + // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } + // ^-------------^ + body_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + } else { + // Problem getting source text. Pretend a comment was found. + true } } -impl UndocumentedUnsafeBlocks { - fn block_has_safety_comment(&mut self, tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span: Span) -> Option { - let map = tcx.hir(); - let source_map = tcx.sess.source_map(); - - let enclosing_scope_span = map.opt_span(enclosing_hir_id)?; - - let between_span = if block_span.from_expansion() { - self.macro_expansion = true; - enclosing_scope_span.with_hi(block_span.hi()).source_callsite() - } else { - self.macro_expansion = false; - enclosing_scope_span.to(block_span).source_callsite() - }; - - let file_name = source_map.span_to_filename(between_span); - let source_file = source_map.get_source_file(&file_name)?; - - let lex_start = (between_span.lo().0 - source_file.start_pos.0 + 1) as usize; - let lex_end = (between_span.hi().0 - source_file.start_pos.0) as usize; - let src_str = source_file.src.as_ref()?[lex_start..lex_end].to_string(); - - let source_start_pos = source_file.start_pos.0 as usize + lex_start; - - let mut pos = 0; - let mut comment = false; - - for token in rustc_lexer::tokenize(&src_str) { - match token.kind { - TokenKind::LineComment { doc_style: None } - | TokenKind::BlockComment { - doc_style: None, - terminated: true, - } => { - let comment_str = src_str[pos + 2..pos + token.len].to_ascii_uppercase(); - - if comment_str.contains("SAFETY:") { - comment = true; - } - }, - // We need to add all whitespace to `pos` before checking the comment's line number - TokenKind::Whitespace => {}, - _ => { - if comment { - // Get the line number of the "comment" (really wherever the trailing whitespace ended) - let comment_line_num = source_file - .lookup_file_pos(BytePos((source_start_pos + pos).try_into().unwrap())) - .0; - // Find the block/local's line number - let block_line_num = tcx.sess.source_map().lookup_char_pos(block_span.lo()).line; - - // Check the comment is immediately followed by the block/local - if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num { - return Some(true); - } - - comment = false; - } - }, +/// Checks if the given text has a safety comment for the immediately proceeding line. +fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool { + let mut lines = line_starts + .array_windows::<2>() + .rev() + .map_while(|[start, end]| { + src.get(start.to_usize() - offset..end.to_usize() - offset) + .map(|text| (start.to_usize(), text.trim_start())) + }) + .filter(|(_, text)| !text.is_empty()); + + let Some((line_start, line)) = lines.next() else { + return false; + }; + // Check for a sequence of line comments. + if line.starts_with("//") { + let mut line = line; + loop { + if line.to_ascii_uppercase().contains("SAFETY:") { + return true; + } + match lines.next() { + Some((_, x)) if x.starts_with("//") => line = x, + _ => return false, } - - pos += token.len; } - - Some(false) } - - fn lint(&self, cx: &LateContext<'_>, mut span: Span) { - let source_map = cx.tcx.sess.source_map(); - - if source_map.is_multiline(span) { - span = source_map.span_until_char(span, '\n'); + // No line comments; look for the start of a block comment. + // This will only find them if they are at the start of a line. + let (mut line_start, mut line) = (line_start, line); + loop { + if line.starts_with("/*") { + let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start(); + let mut tokens = tokenize(src); + return src[..tokens.next().unwrap().len] + .to_ascii_uppercase() + .contains("SAFETY:") + && tokens.all(|t| t.kind == TokenKind::Whitespace); } - - if self.macro_expansion { - span_lint_and_help( - cx, - UNDOCUMENTED_UNSAFE_BLOCKS, - span, - "unsafe block in macro expansion missing a safety comment", - None, - "consider adding a safety comment in the macro definition", - ); - } else { - let block_indent = indent_of(cx, span); - let suggestion = format!("// SAFETY: ...\n{}", snippet(cx, span, "..")); - - span_lint_and_sugg( - cx, - UNDOCUMENTED_UNSAFE_BLOCKS, - span, - "unsafe block missing a safety comment", - "consider adding a safety comment", - reindent_multiline(Cow::Borrowed(&suggestion), true, block_indent).to_string(), - Applicability::HasPlaceholders, - ); + match lines.next() { + Some(x) => (line_start, line) = x, + None => return false, } } } diff --git a/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs b/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs deleted file mode 100644 index db652766705c4..0000000000000 --- a/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs +++ /dev/null @@ -1,59 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_res; -use clippy_utils::ty::is_type_lang_item; -use rustc_hir::{lang_items, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. - /// - /// ### Why is this bad? - /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`. - /// - /// ### Known problems - /// Does not catch cases if the user binds `std::mem::drop` - /// to a different name and calls it that way. - /// - /// ### Example - /// ```rust - /// struct S; - /// drop(std::mem::ManuallyDrop::new(S)); - /// ``` - /// Use instead: - /// ```rust - /// struct S; - /// unsafe { - /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); - /// } - /// ``` - #[clippy::version = "1.49.0"] - pub UNDROPPED_MANUALLY_DROPS, - correctness, - "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value" -} - -declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); - -impl<'tcx> LateLintPass<'tcx> for UndroppedManuallyDrops { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(fun, [arg_0, ..]) = expr.kind; - if path_res(cx, fun).opt_def_id() == cx.tcx.get_diagnostic_item(sym::mem_drop); - let ty = cx.typeck_results().expr_ty(arg_0); - if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop); - then { - span_lint_and_help( - cx, - UNDROPPED_MANUALLY_DROPS, - expr.span, - "the inner value of this ManuallyDrop will not be dropped", - None, - "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop", - ); - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 09d671e11184d..f8e1021af0ea1 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -34,7 +34,7 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// struct Foo {} + /// struct Foo; /// impl Foo { /// fn new() -> Foo { /// Foo {} @@ -43,7 +43,7 @@ declare_clippy_lint! { /// ``` /// could be /// ```rust - /// struct Foo {} + /// struct Foo; /// impl Foo { /// fn new() -> Self { /// Self {} diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 680b2eb1da723..271c3a3dd181c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -156,7 +156,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED. /// /// The minimum rust version that the project supports (msrv: Option = None), diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index b3b241392fed5..25d74b8c49939 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -25,7 +25,7 @@ use rustc_hir::{ use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::mir::interpret::ConstValue; -use rustc_middle::ty::{self, subst::GenericArgKind}; +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy}; use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; @@ -889,7 +889,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { + Res::Def(DefKind::Const | DefKind::Static(..), def_id) => { if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) { if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind { let body = cx.tcx.hir().body(body_id); @@ -934,7 +934,16 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { // implementations of native types. Check lang items. let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); let lang_items = cx.tcx.lang_items(); - for item_def_id in lang_items.items().iter().flatten() { + // This list isn't complete, but good enough for our current list of paths. + let incoherent_impls = [ + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), + SimplifiedTypeGen::SliceSimplifiedType, + SimplifiedTypeGen::StrSimplifiedType, + ] + .iter() + .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); + for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { let lang_item_path = cx.get_def_path(*item_def_id); if path_syms.starts_with(&lang_item_path) { if let [item] = &path_syms[lang_item_path.len()..] { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index b3fad6ce7b651..ca03b8010dd82 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -756,7 +756,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> { let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)); if match_type(self.cx, expr_ty, &paths::LINT); then { - if let hir::def::Res::Def(DefKind::Static, _) = path.res { + if let hir::def::Res::Def(DefKind::Static(..), _) = path.res { let lint_name = last_path_segment(qpath).ident.name; self.lints.push(sym_to_string(lint_name).to_ascii_lowercase()); } else if let Some(local) = get_parent_local(self.cx, expr) { diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index d3ed8da4499f8..0b1fd95c3453d 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.61" +version = "0.1.62" edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 62e144398012d..a275bac4ce63d 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -77,19 +77,22 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, - ExprKind, FnDecl, ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, - MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, - TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, + def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl, + ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, + Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, + TraitRef, TyKind, UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; use rustc_middle::ty as rustc_ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::{IntTy, UintTy, FloatTy}; -use rustc_middle::ty::fast_reject::SimplifiedTypeGen::*; +use rustc_middle::ty::fast_reject::SimplifiedTypeGen::{ + ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType, + PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType, +}; use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture}; +use rustc_middle::ty::{FloatTy, IntTy, UintTy}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -522,7 +525,7 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { let tcx = cx.tcx; let starts = find_primitive(tcx, base) .chain(find_crate(tcx, base)) - .flat_map(|id| item_child_by_name(tcx, id, first)); + .filter_map(|id| item_child_by_name(tcx, id, first)); for first in starts { let last = path diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index fce93153d96ec..0424e06720263 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -14,7 +14,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,53,0 { OR_PATTERNS, MANUAL_BITS } 1,52,0 { STR_SPLIT_ONCE } - 1,51,0 { BORROW_AS_PTR } + 1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS } 1,50,0 { BOOL_THEN } 1,47,0 { TAU } 1,46,0 { CONST_IF_MATCH } @@ -30,6 +30,6 @@ msrv_aliases! { 1,34,0 { TRY_FROM } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,28,0 { FROM_BOOL } - 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST } + 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 6f56f8d51365a..79e6e92dc0aaf 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -105,6 +105,8 @@ pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"]; pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"]; pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"]; pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"]; +pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"]; +pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"]; pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"]; pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"]; pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"]; diff --git a/src/tools/clippy/doc/release.md b/src/tools/clippy/doc/release.md index afe3033c288cf..c4f8f98938428 100644 --- a/src/tools/clippy/doc/release.md +++ b/src/tools/clippy/doc/release.md @@ -121,4 +121,25 @@ happened a stable backport, make sure to re-merge those changes just as with the For this see the document on [how to update the changelog]. +If you don't have time to do a complete changelog update right away, just update +the following parts: + +- Remove the `(beta)` from the new stable version: + + ```markdown + ## Rust 1.XX (beta) -> ## Rust 1.XX + ``` + +- Update the release date line of the new stable version: + + ```markdown + Current beta, release 20YY-MM-DD -> Current stable, released 20YY-MM-DD + ``` + +- Update the release date line of the previous stable version: + + ```markdown + Current stable, released 20YY-MM-DD -> Released 20YY-MM-DD + ``` + [how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 5befb856a0234..bb29c71e9f455 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-03-24" +channel = "nightly-2022-04-07" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index bc1b0d7457559..00dc916b217ca 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -165,8 +165,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); - let fallback_bundle = rustc_errors::fallback_fluent_bundle(false) - .expect("failed to load fallback fluent bundle"); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle"); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, diff --git a/src/tools/clippy/tests/fmt.rs b/src/tools/clippy/tests/check-fmt.rs similarity index 100% rename from src/tools/clippy/tests/fmt.rs rename to src/tools/clippy/tests/check-fmt.rs diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr index af4c298b31085..e2010e9981315 100644 --- a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr +++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr @@ -1,19 +1,19 @@ -error: `mod.rs` files are required, found `/bad/inner.rs` +error: `mod.rs` files are required, found `bad/inner.rs` --> $DIR/bad/inner.rs:1:1 | LL | pub mod stuff; | ^ | = note: `-D clippy::self-named-module-files` implied by `-D warnings` - = help: move `/bad/inner.rs` to `/bad/inner/mod.rs` + = help: move `bad/inner.rs` to `bad/inner/mod.rs` -error: `mod.rs` files are required, found `/bad/inner/stuff.rs` +error: `mod.rs` files are required, found `bad/inner/stuff.rs` --> $DIR/bad/inner/stuff.rs:1:1 | LL | pub mod most; | ^ | - = help: move `/bad/inner/stuff.rs` to `/bad/inner/stuff/mod.rs` + = help: move `bad/inner/stuff.rs` to `bad/inner/stuff/mod.rs` error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr index 11e15db7fb96b..f91940209383f 100644 --- a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr +++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr @@ -1,11 +1,11 @@ -error: `mod.rs` files are not allowed, found `/bad/mod.rs` +error: `mod.rs` files are not allowed, found `bad/mod.rs` --> $DIR/bad/mod.rs:1:1 | LL | pub struct Thing; | ^ | = note: `-D clippy::mod-module-files` implied by `-D warnings` - = help: move `/bad/mod.rs` to `/bad.rs` + = help: move `bad/mod.rs` to `bad.rs` error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr index 9302e02ccb9c4..67e1a07b7f5fa 100644 --- a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr +++ b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr @@ -46,11 +46,6 @@ LL | | report_in_external_macro: true LL | | } | |_^ | -note: the lint level is defined here - --> $DIR/check_clippy_version_attribute.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ = 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) diff --git a/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.rs b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.rs index 242984680e163..32dd80246fab4 100644 --- a/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.rs +++ b/src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.rs @@ -4,6 +4,6 @@ struct S { a: bool, } -struct Foo {} +struct Foo; fn main() {} diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_unsafe.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_unsafe.rs new file mode 100644 index 0000000000000..3c40f77469b82 --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_unsafe.rs @@ -0,0 +1,18 @@ +// compile-flags: --emit=link +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree}; + +#[proc_macro] +pub fn unsafe_block(input: TokenStream) -> TokenStream { + let span = input.into_iter().next().unwrap().span(); + TokenStream::from_iter([TokenTree::Ident(Ident::new("unsafe", span)), { + let mut group = Group::new(Delimiter::Brace, TokenStream::new()); + group.set_span(span); + TokenTree::Group(group) + }]) +} diff --git a/src/tools/clippy/tests/ui/bytes_nth.stderr b/src/tools/clippy/tests/ui/bytes_nth.stderr index 8a7afa934502d..9851d4791d8db 100644 --- a/src/tools/clippy/tests/ui/bytes_nth.stderr +++ b/src/tools/clippy/tests/ui/bytes_nth.stderr @@ -1,4 +1,4 @@ -error: called `.byte().nth()` on a `String` +error: called `.bytes().nth()` on a `String` --> $DIR/bytes_nth.rs:8:13 | LL | let _ = s.bytes().nth(3); @@ -6,13 +6,13 @@ LL | let _ = s.bytes().nth(3); | = note: `-D clippy::bytes-nth` implied by `-D warnings` -error: called `.byte().nth()` on a `String` +error: called `.bytes().nth()` on a `String` --> $DIR/bytes_nth.rs:9:14 | LL | let _ = &s.bytes().nth(3); | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)` -error: called `.byte().nth()` on a `str` +error: called `.bytes().nth()` on a `str` --> $DIR/bytes_nth.rs:10:13 | LL | let _ = s[..].bytes().nth(3); diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs index 68719c2bc6d05..0d65071af15ed 100644 --- a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -2,7 +2,7 @@ use std::string::String; -struct TestStruct {} +struct TestStruct; impl TestStruct { fn ends_with(self, arg: &str) {} diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs index 2e31ad3172ee8..cf85a5ca931dd 100644 --- a/src/tools/clippy/tests/ui/cast.rs +++ b/src/tools/clippy/tests/ui/cast.rs @@ -7,7 +7,7 @@ clippy::cast_sign_loss, clippy::cast_possible_wrap )] -#[allow(clippy::no_effect, clippy::unnecessary_operation)] +#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)] fn main() { // Test clippy::cast_precision_loss let x0 = 1i32; diff --git a/src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed new file mode 100644 index 0000000000000..4ec2465be06db --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed @@ -0,0 +1,8 @@ +// run-rustfix +#![warn(clippy::cast_abs_to_unsigned)] + +fn main() { + let x: i32 = -42; + let y: u32 = x.unsigned_abs(); + println!("The absolute value of {} is {}", x, y); +} diff --git a/src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs new file mode 100644 index 0000000000000..59b9c8c367883 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs @@ -0,0 +1,8 @@ +// run-rustfix +#![warn(clippy::cast_abs_to_unsigned)] + +fn main() { + let x: i32 = -42; + let y: u32 = x.abs() as u32; + println!("The absolute value of {} is {}", x, y); +} diff --git a/src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr new file mode 100644 index 0000000000000..eb12857357a44 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr @@ -0,0 +1,10 @@ +error: casting the result of `i32::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:6:18 + | +LL | let y: u32 = x.abs() as u32; + | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` + | + = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/cast_alignment.rs b/src/tools/clippy/tests/ui/cast_alignment.rs index 659591fffbecd..e4e7290a30e9e 100644 --- a/src/tools/clippy/tests/ui/cast_alignment.rs +++ b/src/tools/clippy/tests/ui/cast_alignment.rs @@ -1,6 +1,7 @@ //! Test casts for alignment issues #![feature(rustc_private)] +#![feature(core_intrinsics)] extern crate libc; #[warn(clippy::cast_ptr_alignment)] @@ -34,4 +35,17 @@ fn main() { (&1u32 as *const u32 as *const libc::c_void) as *const u32; // For ZST, we should trust the user. See #4256 (&1u32 as *const u32 as *const ()) as *const u32; + + // Issue #2881 + let mut data = [0u8, 0u8]; + unsafe { + let ptr = &data as *const [u8; 2] as *const u8; + let _ = (ptr as *const u16).read_unaligned(); + let _ = core::ptr::read_unaligned(ptr as *const u16); + let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16); + let ptr = &mut data as *mut [u8; 2] as *mut u8; + let _ = (ptr as *mut u16).write_unaligned(0); + let _ = core::ptr::write_unaligned(ptr as *mut u16, 0); + let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0); + } } diff --git a/src/tools/clippy/tests/ui/cast_alignment.stderr b/src/tools/clippy/tests/ui/cast_alignment.stderr index aedd368445554..5df2b5b1094be 100644 --- a/src/tools/clippy/tests/ui/cast_alignment.stderr +++ b/src/tools/clippy/tests/ui/cast_alignment.stderr @@ -1,5 +1,5 @@ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:18:5 + --> $DIR/cast_alignment.rs:19:5 | LL | (&1u8 as *const u8) as *const u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,19 +7,19 @@ LL | (&1u8 as *const u8) as *const u16; = note: `-D clippy::cast-ptr-alignment` implied by `-D warnings` error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:19:5 + --> $DIR/cast_alignment.rs:20:5 | LL | (&mut 1u8 as *mut u8) as *mut u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:22:5 + --> $DIR/cast_alignment.rs:23:5 | LL | (&1u8 as *const u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:23:5 + --> $DIR/cast_alignment.rs:24:5 | LL | (&mut 1u8 as *mut u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/crashes/ice-2774.rs b/src/tools/clippy/tests/ui/crashes/ice-2774.rs index d44b0fae82001..88cfa1f923c0b 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-2774.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-2774.rs @@ -8,7 +8,7 @@ pub struct Bar { } #[derive(Eq, PartialEq, Debug, Hash)] -pub struct Foo {} +pub struct Foo; #[allow(clippy::implicit_hasher)] // This should not cause a "cannot relate bound region" ICE. diff --git a/src/tools/clippy/tests/ui/crashes/ice-6179.rs b/src/tools/clippy/tests/ui/crashes/ice-6179.rs index 8d9a1af8ff118..4fe92d356c44d 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6179.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-6179.rs @@ -4,7 +4,7 @@ #![warn(clippy::use_self)] #![allow(dead_code)] -struct Foo {} +struct Foo; impl Foo { fn new() -> Self { diff --git a/src/tools/clippy/tests/ui/crashes/ice-6792.rs b/src/tools/clippy/tests/ui/crashes/ice-6792.rs index 0e2ab1a39b82f..9cbafc716b500 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6792.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-6792.rs @@ -7,7 +7,7 @@ trait Trait { fn broken() -> Self::Ty; } -struct Foo {} +struct Foo; impl Trait for Foo { type Ty = Foo; diff --git a/src/tools/clippy/tests/ui/crashes/ice-7868.stderr b/src/tools/clippy/tests/ui/crashes/ice-7868.stderr index 111350a6280dc..1a33e647588f1 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-7868.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-7868.stderr @@ -5,11 +5,7 @@ LL | unsafe { 0 }; | ^^^^^^^^^^^^ | = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL ~ unsafe { 0 }; - | + = help: consider adding a safety comment on the preceding line error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs index 676564b2445d5..376ff97ba6036 100644 --- a/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs +++ b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs @@ -3,7 +3,7 @@ trait Foo {} -struct Bar {} +struct Bar; struct Baz<'a> { bar: &'a Bar, diff --git a/src/tools/clippy/tests/ui/crashes/regressions.rs b/src/tools/clippy/tests/ui/crashes/regressions.rs index a41bcb33b4460..6f9d98bbfe7f3 100644 --- a/src/tools/clippy/tests/ui/crashes/regressions.rs +++ b/src/tools/clippy/tests/ui/crashes/regressions.rs @@ -6,6 +6,6 @@ pub fn foo(bar: *const u8) { // Regression test for https://github.com/rust-lang/rust-clippy/issues/4917 /// { + println!("{}", $crate::hygienic::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +mod unhygienic { + #[macro_export] + macro_rules! print_message_unhygienic { + () => { + println!("{}", $crate::unhygienic::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +mod unhygienic_intentionally { + // For cases where the use of `crate` is intentional, applying `allow` to the macro definition + // should suppress the lint. + #[allow(clippy::crate_in_macro_def)] + #[macro_export] + macro_rules! print_message_unhygienic_intentionally { + () => { + println!("{}", crate::CALLER_PROVIDED_MESSAGE); + }; + } +} + +#[macro_use] +mod not_exported { + macro_rules! print_message_not_exported { + () => { + println!("{}", crate::not_exported::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +fn main() { + print_message_hygienic!(); + print_message_unhygienic!(); + print_message_unhygienic_intentionally!(); + print_message_not_exported!(); +} + +pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!"; diff --git a/src/tools/clippy/tests/ui/crate_in_macro_def.rs b/src/tools/clippy/tests/ui/crate_in_macro_def.rs new file mode 100644 index 0000000000000..ac456108e4ab1 --- /dev/null +++ b/src/tools/clippy/tests/ui/crate_in_macro_def.rs @@ -0,0 +1,56 @@ +// run-rustfix +#![warn(clippy::crate_in_macro_def)] + +mod hygienic { + #[macro_export] + macro_rules! print_message_hygienic { + () => { + println!("{}", $crate::hygienic::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +mod unhygienic { + #[macro_export] + macro_rules! print_message_unhygienic { + () => { + println!("{}", crate::unhygienic::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +mod unhygienic_intentionally { + // For cases where the use of `crate` is intentional, applying `allow` to the macro definition + // should suppress the lint. + #[allow(clippy::crate_in_macro_def)] + #[macro_export] + macro_rules! print_message_unhygienic_intentionally { + () => { + println!("{}", crate::CALLER_PROVIDED_MESSAGE); + }; + } +} + +#[macro_use] +mod not_exported { + macro_rules! print_message_not_exported { + () => { + println!("{}", crate::not_exported::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +fn main() { + print_message_hygienic!(); + print_message_unhygienic!(); + print_message_unhygienic_intentionally!(); + print_message_not_exported!(); +} + +pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!"; diff --git a/src/tools/clippy/tests/ui/crate_in_macro_def.stderr b/src/tools/clippy/tests/ui/crate_in_macro_def.stderr new file mode 100644 index 0000000000000..9ac5937dcc063 --- /dev/null +++ b/src/tools/clippy/tests/ui/crate_in_macro_def.stderr @@ -0,0 +1,10 @@ +error: `crate` references the macro call's crate + --> $DIR/crate_in_macro_def.rs:19:28 + | +LL | println!("{}", crate::unhygienic::MESSAGE); + | ^^^^^ help: to reference the macro definition's crate, use: `$crate` + | + = note: `-D clippy::crate-in-macro-def` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed index 1b0e7544e79c6..e0b4a2f694239 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed @@ -134,7 +134,7 @@ mod enum_ctor { } mod method_calls { - struct StructForMethodCallTest {} + struct StructForMethodCallTest; impl StructForMethodCallTest { fn concrete_arg(&self, f: f64) {} diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs index e9687777bbd0b..50bbb6eec6c70 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs @@ -134,7 +134,7 @@ mod enum_ctor { } mod method_calls { - struct StructForMethodCallTest {} + struct StructForMethodCallTest; impl StructForMethodCallTest { fn concrete_arg(&self, f: f64) {} diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed index 55c082fcb19fb..bded9e2c0e801 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed @@ -133,7 +133,7 @@ mod enum_ctor { } mod method_calls { - struct StructForMethodCallTest {} + struct StructForMethodCallTest; impl StructForMethodCallTest { fn concrete_arg(&self, x: i32) {} diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs index e0a4828ce9ff6..3fceefa551c78 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs @@ -133,7 +133,7 @@ mod enum_ctor { } mod method_calls { - struct StructForMethodCallTest {} + struct StructForMethodCallTest; impl StructForMethodCallTest { fn concrete_arg(&self, x: i32) {} diff --git a/src/tools/clippy/tests/ui/drop_forget_copy.rs b/src/tools/clippy/tests/ui/drop_forget_copy.rs index 9ddd6d64701a6..7c7a9ecff67f5 100644 --- a/src/tools/clippy/tests/ui/drop_forget_copy.rs +++ b/src/tools/clippy/tests/ui/drop_forget_copy.rs @@ -5,7 +5,7 @@ use std::mem::{drop, forget}; use std::vec::Vec; #[derive(Copy, Clone)] -struct SomeStruct {} +struct SomeStruct; struct AnotherStruct { x: u8, diff --git a/src/tools/clippy/tests/ui/drop_forget_copy.stderr b/src/tools/clippy/tests/ui/drop_forget_copy.stderr index 01de0be7caea9..88228afae89c0 100644 --- a/src/tools/clippy/tests/ui/drop_forget_copy.stderr +++ b/src/tools/clippy/tests/ui/drop_forget_copy.stderr @@ -5,7 +5,7 @@ LL | drop(s1); | ^^^^^^^^ | = note: `-D clippy::drop-copy` implied by `-D warnings` -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:33:10 | LL | drop(s1); @@ -17,7 +17,7 @@ error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a LL | drop(s2); | ^^^^^^^^ | -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:34:10 | LL | drop(s2); @@ -29,7 +29,7 @@ error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a LL | drop(s4); | ^^^^^^^^ | -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:36:10 | LL | drop(s4); @@ -42,7 +42,7 @@ LL | forget(s1); | ^^^^^^^^^^ | = note: `-D clippy::forget-copy` implied by `-D warnings` -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:39:12 | LL | forget(s1); @@ -54,7 +54,7 @@ error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetti LL | forget(s2); | ^^^^^^^^^^ | -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:40:12 | LL | forget(s2); @@ -66,7 +66,7 @@ error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetti LL | forget(s4); | ^^^^^^^^^^ | -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:42:12 | LL | forget(s4); diff --git a/src/tools/clippy/tests/ui/drop_non_drop.rs b/src/tools/clippy/tests/ui/drop_non_drop.rs new file mode 100644 index 0000000000000..5a0ebde82c5d4 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_non_drop.rs @@ -0,0 +1,40 @@ +#![warn(clippy::drop_non_drop)] + +use core::mem::drop; + +fn make_result(t: T) -> Result { + Ok(t) +} + +#[must_use] +fn must_use(t: T) -> T { + t +} + +fn drop_generic(t: T) { + // Don't lint + drop(t) +} + +fn main() { + struct Foo; + // Lint + drop(Foo); + // Don't lint + drop(make_result(Foo)); + // Don't lint + drop(must_use(Foo)); + + struct Bar; + impl Drop for Bar { + fn drop(&mut self) {} + } + // Don't lint + drop(Bar); + + struct Baz(T); + // Lint + drop(Baz(Foo)); + // Don't lint + drop(Baz(Bar)); +} diff --git a/src/tools/clippy/tests/ui/drop_non_drop.stderr b/src/tools/clippy/tests/ui/drop_non_drop.stderr new file mode 100644 index 0000000000000..f73068901c503 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_non_drop.stderr @@ -0,0 +1,27 @@ +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes + --> $DIR/drop_non_drop.rs:22:5 + | +LL | drop(Foo); + | ^^^^^^^^^ + | + = note: `-D clippy::drop-non-drop` implied by `-D warnings` +note: argument has type `main::Foo` + --> $DIR/drop_non_drop.rs:22:10 + | +LL | drop(Foo); + | ^^^ + +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes + --> $DIR/drop_non_drop.rs:37:5 + | +LL | drop(Baz(Foo)); + | ^^^^^^^^^^^^^^ + | +note: argument has type `main::Baz` + --> $DIR/drop_non_drop.rs:37:10 + | +LL | drop(Baz(Foo)); + | ^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/drop_ref.rs b/src/tools/clippy/tests/ui/drop_ref.rs index e1a15c609fd23..7de0b0bbdf9ae 100644 --- a/src/tools/clippy/tests/ui/drop_ref.rs +++ b/src/tools/clippy/tests/ui/drop_ref.rs @@ -1,7 +1,7 @@ #![warn(clippy::drop_ref)] #![allow(clippy::toplevel_ref_arg)] #![allow(clippy::map_err_ignore)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::drop_non_drop)] use std::mem::drop; diff --git a/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed b/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed new file mode 100644 index 0000000000000..80f07603b8d4f --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_structs_with_brackets.fixed @@ -0,0 +1,25 @@ +// run-rustfix +#![warn(clippy::empty_structs_with_brackets)] +#![allow(dead_code)] + +pub struct MyEmptyStruct; // should trigger lint +struct MyEmptyTupleStruct; // should trigger lint + +// should not trigger lint +struct MyCfgStruct { + #[cfg(feature = "thisisneverenabled")] + field: u8, +} + +// should not trigger lint +struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8); + +// should not trigger lint +struct MyStruct { + field: u8, +} +struct MyTupleStruct(usize, String); // should not trigger lint +struct MySingleTupleStruct(usize); // should not trigger lint +struct MyUnitLikeStruct; // should not trigger lint + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs b/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs new file mode 100644 index 0000000000000..1d1ed4c769025 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_structs_with_brackets.rs @@ -0,0 +1,25 @@ +// run-rustfix +#![warn(clippy::empty_structs_with_brackets)] +#![allow(dead_code)] + +pub struct MyEmptyStruct {} // should trigger lint +struct MyEmptyTupleStruct(); // should trigger lint + +// should not trigger lint +struct MyCfgStruct { + #[cfg(feature = "thisisneverenabled")] + field: u8, +} + +// should not trigger lint +struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8); + +// should not trigger lint +struct MyStruct { + field: u8, +} +struct MyTupleStruct(usize, String); // should not trigger lint +struct MySingleTupleStruct(usize); // should not trigger lint +struct MyUnitLikeStruct; // should not trigger lint + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_structs_with_brackets.stderr b/src/tools/clippy/tests/ui/empty_structs_with_brackets.stderr new file mode 100644 index 0000000000000..0308cb5571af2 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_structs_with_brackets.stderr @@ -0,0 +1,19 @@ +error: found empty brackets on struct declaration + --> $DIR/empty_structs_with_brackets.rs:5:25 + | +LL | pub struct MyEmptyStruct {} // should trigger lint + | ^^^ + | + = note: `-D clippy::empty-structs-with-brackets` implied by `-D warnings` + = help: remove the brackets + +error: found empty brackets on struct declaration + --> $DIR/empty_structs_with_brackets.rs:6:26 + | +LL | struct MyEmptyTupleStruct(); // should trigger lint + | ^^^ + | + = help: remove the brackets + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/err_expect.fixed b/src/tools/clippy/tests/ui/err_expect.fixed new file mode 100644 index 0000000000000..7e18d70bae400 --- /dev/null +++ b/src/tools/clippy/tests/ui/err_expect.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +struct MyTypeNonDebug; + +#[derive(Debug)] +struct MyTypeDebug; + +fn main() { + let test_debug: Result = Ok(MyTypeDebug); + test_debug.expect_err("Testing debug type"); + + let test_non_debug: Result = Ok(MyTypeNonDebug); + test_non_debug.err().expect("Testing non debug type"); +} diff --git a/src/tools/clippy/tests/ui/err_expect.rs b/src/tools/clippy/tests/ui/err_expect.rs new file mode 100644 index 0000000000000..bf8c3c9fb8c98 --- /dev/null +++ b/src/tools/clippy/tests/ui/err_expect.rs @@ -0,0 +1,14 @@ +// run-rustfix + +struct MyTypeNonDebug; + +#[derive(Debug)] +struct MyTypeDebug; + +fn main() { + let test_debug: Result = Ok(MyTypeDebug); + test_debug.err().expect("Testing debug type"); + + let test_non_debug: Result = Ok(MyTypeNonDebug); + test_non_debug.err().expect("Testing non debug type"); +} diff --git a/src/tools/clippy/tests/ui/err_expect.stderr b/src/tools/clippy/tests/ui/err_expect.stderr new file mode 100644 index 0000000000000..ffd97e00a5c09 --- /dev/null +++ b/src/tools/clippy/tests/ui/err_expect.stderr @@ -0,0 +1,10 @@ +error: called `.err().expect()` on a `Result` value + --> $DIR/err_expect.rs:10:16 + | +LL | test_debug.err().expect("Testing debug type"); + | ^^^^^^^^^^^^ help: try: `expect_err` + | + = note: `-D clippy::err-expect` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs b/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs index 1442ee08e7546..f805bcc9ba8af 100644 --- a/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs +++ b/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs @@ -20,7 +20,7 @@ fn h(_: bool, _: bool, _: bool) {} fn e(_: S, _: S, _: Box, _: Vec) {} fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} -struct S {} +struct S; trait Trait { fn f(_: bool, _: bool, _: bool, _: bool); fn g(_: bool, _: bool, _: bool, _: Vec); diff --git a/src/tools/clippy/tests/ui/forget_non_drop.rs b/src/tools/clippy/tests/ui/forget_non_drop.rs new file mode 100644 index 0000000000000..7580cf95ebfa8 --- /dev/null +++ b/src/tools/clippy/tests/ui/forget_non_drop.rs @@ -0,0 +1,27 @@ +#![warn(clippy::forget_non_drop)] + +use core::mem::forget; + +fn forget_generic(t: T) { + // Don't lint + forget(t) +} + +fn main() { + struct Foo; + // Lint + forget(Foo); + + struct Bar; + impl Drop for Bar { + fn drop(&mut self) {} + } + // Don't lint + forget(Bar); + + struct Baz(T); + // Lint + forget(Baz(Foo)); + // Don't lint + forget(Baz(Bar)); +} diff --git a/src/tools/clippy/tests/ui/forget_non_drop.stderr b/src/tools/clippy/tests/ui/forget_non_drop.stderr new file mode 100644 index 0000000000000..03fb00960a447 --- /dev/null +++ b/src/tools/clippy/tests/ui/forget_non_drop.stderr @@ -0,0 +1,27 @@ +error: call to `std::mem::forget` with a value that does not implement `Drop`. Forgetting such a type is the same as dropping it + --> $DIR/forget_non_drop.rs:13:5 + | +LL | forget(Foo); + | ^^^^^^^^^^^ + | + = note: `-D clippy::forget-non-drop` implied by `-D warnings` +note: argument has type `main::Foo` + --> $DIR/forget_non_drop.rs:13:12 + | +LL | forget(Foo); + | ^^^ + +error: call to `std::mem::forget` with a value that does not implement `Drop`. Forgetting such a type is the same as dropping it + --> $DIR/forget_non_drop.rs:24:5 + | +LL | forget(Baz(Foo)); + | ^^^^^^^^^^^^^^^^ + | +note: argument has type `main::Baz` + --> $DIR/forget_non_drop.rs:24:12 + | +LL | forget(Baz(Foo)); + | ^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/forget_ref.rs b/src/tools/clippy/tests/ui/forget_ref.rs index c49e6756a6c5b..6c8c4c9c0edec 100644 --- a/src/tools/clippy/tests/ui/forget_ref.rs +++ b/src/tools/clippy/tests/ui/forget_ref.rs @@ -1,6 +1,6 @@ #![warn(clippy::forget_ref)] #![allow(clippy::toplevel_ref_arg)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::forget_non_drop)] use std::mem::forget; diff --git a/src/tools/clippy/tests/ui/identity_op.rs b/src/tools/clippy/tests/ui/identity_op.rs index 12bbda71f4348..edc3fe1aec13a 100644 --- a/src/tools/clippy/tests/ui/identity_op.rs +++ b/src/tools/clippy/tests/ui/identity_op.rs @@ -66,4 +66,13 @@ fn main() { let b = a << 0; // no error: non-integer 1 * Meter; // no error: non-integer + + 2 % 3; + -2 % 3; + 2 % -3 + x; + -2 % -3 + x; + x + 1 % 3; + (x + 1) % 3; // no error + 4 % 3; // no error + 4 % -3; // no error } diff --git a/src/tools/clippy/tests/ui/identity_op.stderr b/src/tools/clippy/tests/ui/identity_op.stderr index 0103cf5457e81..706f01a3dd6c4 100644 --- a/src/tools/clippy/tests/ui/identity_op.stderr +++ b/src/tools/clippy/tests/ui/identity_op.stderr @@ -78,5 +78,35 @@ error: the operation is ineffective. Consider reducing it to `x` LL | x >> &0; | ^^^^^^^ -error: aborting due to 13 previous errors +error: the operation is ineffective. Consider reducing it to `2` + --> $DIR/identity_op.rs:70:5 + | +LL | 2 % 3; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `-2` + --> $DIR/identity_op.rs:71:5 + | +LL | -2 % 3; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `2` + --> $DIR/identity_op.rs:72:5 + | +LL | 2 % -3 + x; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `-2` + --> $DIR/identity_op.rs:73:5 + | +LL | -2 % -3 + x; + | ^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:74:9 + | +LL | x + 1 % 3; + | ^^^^^ + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/implicit_clone.rs b/src/tools/clippy/tests/ui/implicit_clone.rs index 639fecb8927bd..2549c9f32f904 100644 --- a/src/tools/clippy/tests/ui/implicit_clone.rs +++ b/src/tools/clippy/tests/ui/implicit_clone.rs @@ -30,7 +30,7 @@ where } #[derive(Copy, Clone)] -struct Kitten {} +struct Kitten; impl Kitten { // badly named method fn to_vec(self) -> Kitten { @@ -44,7 +44,7 @@ impl Borrow for Kitten { } } -struct BorrowedKitten {} +struct BorrowedKitten; impl ToOwned for BorrowedKitten { type Owned = Kitten; fn to_owned(&self) -> Kitten { diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.rs b/src/tools/clippy/tests/ui/indexing_slicing_index.rs index ca8ca53c80c3f..45a430edcb589 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_index.rs +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.rs @@ -1,18 +1,34 @@ +#![feature(inline_const)] #![warn(clippy::indexing_slicing)] // We also check the out_of_bounds_indexing lint here, because it lints similar things and // we want to avoid false positives. #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(const_err, clippy::no_effect, clippy::unnecessary_operation)] + +const ARR: [i32; 2] = [1, 2]; +const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr. +const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + +const fn idx() -> usize { + 1 +} +const fn idx4() -> usize { + 4 +} fn main() { let x = [1, 2, 3, 4]; let index: usize = 1; x[index]; - x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. - x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + x[4]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + x[1 << 3]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. x[0]; // Ok, should not produce stderr. x[3]; // Ok, should not produce stderr. + x[const { idx() }]; // Ok, should not produce stderr. + x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + const { &ARR[idx()] }; // Ok, should not produce stderr. + const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. let y = &x; y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 @@ -25,7 +41,7 @@ fn main() { const N: usize = 15; // Out of bounds const M: usize = 3; // In bounds - x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + x[N]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. x[M]; // Ok, should not produce stderr. v[N]; v[M]; diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr index 76ecec3348400..83a36f407d5d8 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr @@ -1,5 +1,17 @@ +error[E0080]: evaluation of `main::{constant#3}::<&i32>` failed + --> $DIR/indexing_slicing_index.rs:31:14 + | +LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +error[E0080]: erroneous constant used + --> $DIR/indexing_slicing_index.rs:31:5 + | +LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + error: indexing may panic - --> $DIR/indexing_slicing_index.rs:10:5 + --> $DIR/indexing_slicing_index.rs:22:5 | LL | x[index]; | ^^^^^^^^ @@ -8,7 +20,7 @@ LL | x[index]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:22:5 + --> $DIR/indexing_slicing_index.rs:38:5 | LL | v[0]; | ^^^^ @@ -16,7 +28,7 @@ LL | v[0]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:23:5 + --> $DIR/indexing_slicing_index.rs:39:5 | LL | v[10]; | ^^^^^ @@ -24,7 +36,7 @@ LL | v[10]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:24:5 + --> $DIR/indexing_slicing_index.rs:40:5 | LL | v[1 << 3]; | ^^^^^^^^^ @@ -32,7 +44,7 @@ LL | v[1 << 3]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:30:5 + --> $DIR/indexing_slicing_index.rs:46:5 | LL | v[N]; | ^^^^ @@ -40,12 +52,13 @@ LL | v[N]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:31:5 + --> $DIR/indexing_slicing_index.rs:47:5 | LL | v[M]; | ^^^^ | = help: consider using `.get(n)` or `.get_mut(n)` instead -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors +For more information about this error, try `rustc --explain E0080`. diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.fixed b/src/tools/clippy/tests/ui/iter_nth_zero.fixed index b54147c94d192..f23671c26e4cc 100644 --- a/src/tools/clippy/tests/ui/iter_nth_zero.fixed +++ b/src/tools/clippy/tests/ui/iter_nth_zero.fixed @@ -3,7 +3,7 @@ #![warn(clippy::iter_nth_zero)] use std::collections::HashSet; -struct Foo {} +struct Foo; impl Foo { fn nth(&self, index: usize) -> usize { diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.rs b/src/tools/clippy/tests/ui/iter_nth_zero.rs index b92c7d18adb4f..7c968d4984571 100644 --- a/src/tools/clippy/tests/ui/iter_nth_zero.rs +++ b/src/tools/clippy/tests/ui/iter_nth_zero.rs @@ -3,7 +3,7 @@ #![warn(clippy::iter_nth_zero)] use std::collections::HashSet; -struct Foo {} +struct Foo; impl Foo { fn nth(&self, index: usize) -> usize { diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed index a9041671101b5..56761ebbcb80b 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![allow(dead_code)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; @@ -43,3 +44,8 @@ fn main() { // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); } + +// #8527 +fn cloned_flatten(x: Option<&Option>) -> Option { + x.cloned().flatten() +} diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs index dd04e33a4b3ae..98321d889b582 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![allow(dead_code)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; @@ -45,3 +46,8 @@ fn main() { // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); } + +// #8527 +fn cloned_flatten(x: Option<&Option>) -> Option { + x.cloned().flatten() +} diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr index e36b0e36fbdf9..0582700fd16a8 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr @@ -1,5 +1,5 @@ error: called `cloned().last()` on an `Iterator`. It may be more efficient to call `last().cloned()` instead - --> $DIR/iter_overeager_cloned.rs:7:29 + --> $DIR/iter_overeager_cloned.rs:8:29 | LL | let _: Option = vec.iter().cloned().last(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().last().cloned()` @@ -7,13 +7,13 @@ LL | let _: Option = vec.iter().cloned().last(); = note: `-D clippy::iter-overeager-cloned` implied by `-D warnings` error: called `cloned().next()` on an `Iterator`. It may be more efficient to call `next().cloned()` instead - --> $DIR/iter_overeager_cloned.rs:9:29 + --> $DIR/iter_overeager_cloned.rs:10:29 | LL | let _: Option = vec.iter().chain(vec.iter()).cloned().next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().chain(vec.iter()).next().cloned()` error: called `cloned().count()` on an `Iterator`. It may be more efficient to call `count()` instead - --> $DIR/iter_overeager_cloned.rs:11:20 + --> $DIR/iter_overeager_cloned.rs:12:20 | LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").count()` @@ -21,25 +21,25 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); = note: `-D clippy::redundant-clone` implied by `-D warnings` error: called `cloned().take(...)` on an `Iterator`. It may be more efficient to call `take(...).cloned()` instead - --> $DIR/iter_overeager_cloned.rs:13:21 + --> $DIR/iter_overeager_cloned.rs:14:21 | LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().take(2).cloned()` error: called `cloned().skip(...)` on an `Iterator`. It may be more efficient to call `skip(...).cloned()` instead - --> $DIR/iter_overeager_cloned.rs:15:21 + --> $DIR/iter_overeager_cloned.rs:16:21 | LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().skip(2).cloned()` error: called `cloned().nth(...)` on an `Iterator`. It may be more efficient to call `nth(...).cloned()` instead - --> $DIR/iter_overeager_cloned.rs:17:13 + --> $DIR/iter_overeager_cloned.rs:18:13 | LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").nth(2).cloned()` error: called `cloned().flatten()` on an `Iterator`. It may be more efficient to call `flatten().cloned()` instead - --> $DIR/iter_overeager_cloned.rs:19:13 + --> $DIR/iter_overeager_cloned.rs:20:13 | LL | let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] | _____________^ diff --git a/src/tools/clippy/tests/ui/large_types_passed_by_value.rs b/src/tools/clippy/tests/ui/large_types_passed_by_value.rs index e4a2e9df4d7ba..7601b5c66fa35 100644 --- a/src/tools/clippy/tests/ui/large_types_passed_by_value.rs +++ b/src/tools/clippy/tests/ui/large_types_passed_by_value.rs @@ -37,7 +37,7 @@ pub trait PubLargeTypeDevourer { fn devoure_array_in_public(&self, array: [u8; 6666]); } -struct S {} +struct S; impl LargeTypeDevourer for S { fn devoure_array(&self, array: [u8; 6666]) { todo!(); diff --git a/src/tools/clippy/tests/ui/let_and_return.rs b/src/tools/clippy/tests/ui/let_and_return.rs index e3561863c1e1f..bb162adc9adb2 100644 --- a/src/tools/clippy/tests/ui/let_and_return.rs +++ b/src/tools/clippy/tests/ui/let_and_return.rs @@ -88,7 +88,7 @@ mod no_lint_if_stmt_borrows { ret } - struct Bar {} + struct Bar; impl Bar { fn new() -> Self { diff --git a/src/tools/clippy/tests/ui/let_underscore_must_use.rs b/src/tools/clippy/tests/ui/let_underscore_must_use.rs index a842e872a37b1..1edb77c748bfb 100644 --- a/src/tools/clippy/tests/ui/let_underscore_must_use.rs +++ b/src/tools/clippy/tests/ui/let_underscore_must_use.rs @@ -26,7 +26,7 @@ fn h() -> u32 { 0 } -struct S {} +struct S; impl S { #[must_use] diff --git a/src/tools/clippy/tests/ui/manual_async_fn.fixed b/src/tools/clippy/tests/ui/manual_async_fn.fixed index 136cc96be70ca..b7e46a4a8ccc2 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.fixed +++ b/src/tools/clippy/tests/ui/manual_async_fn.fixed @@ -38,7 +38,7 @@ async fn already_async() -> impl Future { async { 42 } } -struct S {} +struct S; impl S { async fn inh_fut() -> i32 { // NOTE: this code is here just to check that the indentation is correct in the suggested fix diff --git a/src/tools/clippy/tests/ui/manual_async_fn.rs b/src/tools/clippy/tests/ui/manual_async_fn.rs index ddc453ffdb750..b05429da66225 100644 --- a/src/tools/clippy/tests/ui/manual_async_fn.rs +++ b/src/tools/clippy/tests/ui/manual_async_fn.rs @@ -52,7 +52,7 @@ async fn already_async() -> impl Future { async { 42 } } -struct S {} +struct S; impl S { fn inh_fut() -> impl Future { async { diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed index 05d6c56f2aca0..7d68978216c9c 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed @@ -78,7 +78,7 @@ fn result_unwrap_or() { (Ok(1) as Result).unwrap_or(42); // method call case, suggestion must not surround Result expr `s.method()` with parentheses - struct S {} + struct S; impl S { fn method(self) -> Option { Some(42) diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.rs b/src/tools/clippy/tests/ui/manual_unwrap_or.rs index 09f62c69b71de..b937fe6f977e5 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.rs @@ -102,7 +102,7 @@ fn result_unwrap_or() { }; // method call case, suggestion must not surround Result expr `s.method()` with parentheses - struct S {} + struct S; impl S { fn method(self) -> Option { Some(42) diff --git a/src/tools/clippy/tests/ui/map_identity.fixed b/src/tools/clippy/tests/ui/map_identity.fixed index 4a1452b25f343..2256e51f2d09c 100644 --- a/src/tools/clippy/tests/ui/map_identity.fixed +++ b/src/tools/clippy/tests/ui/map_identity.fixed @@ -16,6 +16,8 @@ fn main() { let _: Result = Err(2.3).map(|x: i8| { return x + 3; }); + let _: Result = Ok(1); + let _: Result = Ok(1).map_err(|a: u32| a * 42); } fn not_identity(x: &u16) -> u16 { diff --git a/src/tools/clippy/tests/ui/map_identity.rs b/src/tools/clippy/tests/ui/map_identity.rs index 65c7e6e1ea554..ccfdc9ea76d52 100644 --- a/src/tools/clippy/tests/ui/map_identity.rs +++ b/src/tools/clippy/tests/ui/map_identity.rs @@ -18,6 +18,8 @@ fn main() { let _: Result = Err(2.3).map(|x: i8| { return x + 3; }); + let _: Result = Ok(1).map_err(|a| a); + let _: Result = Ok(1).map_err(|a: u32| a * 42); } fn not_identity(x: &u16) -> u16 { diff --git a/src/tools/clippy/tests/ui/map_identity.stderr b/src/tools/clippy/tests/ui/map_identity.stderr index e4a0320cbda55..b6a77281f6de2 100644 --- a/src/tools/clippy/tests/ui/map_identity.stderr +++ b/src/tools/clippy/tests/ui/map_identity.stderr @@ -33,5 +33,11 @@ LL | | return x; LL | | }); | |______^ help: remove the call to `map` -error: aborting due to 5 previous errors +error: unnecessary map of the identity function + --> $DIR/map_identity.rs:21:36 + | +LL | let _: Result = Ok(1).map_err(|a| a); + | ^^^^^^^^^^^^^^^ help: remove the call to `map_err` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/map_unit_fn.rs b/src/tools/clippy/tests/ui/map_unit_fn.rs index 9a74da4e3b8b6..e7f07b50f3ab1 100644 --- a/src/tools/clippy/tests/ui/map_unit_fn.rs +++ b/src/tools/clippy/tests/ui/map_unit_fn.rs @@ -1,5 +1,5 @@ #![allow(unused)] -struct Mappable {} +struct Mappable; impl Mappable { pub fn map(&self) {} diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs index c5f221220ece7..f83c3e0e281ca 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs @@ -99,7 +99,7 @@ pub fn manual_range_contains() { } pub fn use_self() { - struct Foo {} + struct Foo; impl Foo { fn new() -> Foo { @@ -145,6 +145,16 @@ fn int_from_bool() -> u8 { true as u8 } +fn err_expect() { + let x: Result = Ok(10); + x.err().expect("Testing expect_err"); +} + +fn cast_abs_to_unsigned() { + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + fn main() { filter_map_next(); checked_conversion(); @@ -162,6 +172,8 @@ fn main() { missing_const_for_fn(); unnest_or_patterns(); int_from_bool(); + err_expect(); + cast_abs_to_unsigned(); } mod just_under_msrv { diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr index 6b3fdb0844b49..de225eb740d03 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:186:24 + --> $DIR/min_rust_version_attr.rs:198:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:185:9 + --> $DIR/min_rust_version_attr.rs:197:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL ~ assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:198:24 + --> $DIR/min_rust_version_attr.rs:210:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:197:9 + --> $DIR/min_rust_version_attr.rs:209:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_inline.rs b/src/tools/clippy/tests/ui/missing_inline.rs index b73b24b8e0a3b..07f8e3888c998 100644 --- a/src/tools/clippy/tests/ui/missing_inline.rs +++ b/src/tools/clippy/tests/ui/missing_inline.rs @@ -7,8 +7,8 @@ type Typedef = String; pub type PubTypedef = String; -struct Foo {} // ok -pub struct PubFoo {} // ok +struct Foo; // ok +pub struct PubFoo; // ok enum FooE {} // ok pub enum PubFooE {} // ok @@ -63,4 +63,4 @@ impl PubFoo { // do not lint this since users cannot control the external code #[derive(Debug)] -pub struct S {} +pub struct S; diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.rs b/src/tools/clippy/tests/ui/module_name_repetitions.rs index f5908cb5701fb..ebaa77cc283e0 100644 --- a/src/tools/clippy/tests/ui/module_name_repetitions.rs +++ b/src/tools/clippy/tests/ui/module_name_repetitions.rs @@ -7,7 +7,7 @@ mod foo { pub fn foo() {} pub fn foo_bar() {} pub fn bar_foo() {} - pub struct FooCake {} + pub struct FooCake; pub enum CakeFoo {} pub struct Foo7Bar; diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.stderr b/src/tools/clippy/tests/ui/module_name_repetitions.stderr index bdd217a969c05..3f343a3e43018 100644 --- a/src/tools/clippy/tests/ui/module_name_repetitions.stderr +++ b/src/tools/clippy/tests/ui/module_name_repetitions.stderr @@ -15,8 +15,8 @@ LL | pub fn bar_foo() {} error: item name starts with its containing module's name --> $DIR/module_name_repetitions.rs:10:5 | -LL | pub struct FooCake {} - | ^^^^^^^^^^^^^^^^^^^^^ +LL | pub struct FooCake; + | ^^^^^^^^^^^^^^^^^^^ error: item name ends with its containing module's name --> $DIR/module_name_repetitions.rs:11:5 diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs index 047a29fa1e327..3ebe46bc5be7c 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs @@ -1,5 +1,10 @@ #![warn(clippy::modulo_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::modulo_one, + clippy::identity_op +)] fn main() { // Lint when both sides are const and of the opposite sign diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr index 64335f35f0f82..11b5f77461ba2 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr @@ -1,5 +1,5 @@ error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:6:5 + --> $DIR/modulo_arithmetic_integral_const.rs:11:5 | LL | -1 % 2; | ^^^^^^ @@ -9,7 +9,7 @@ LL | -1 % 2; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:7:5 + --> $DIR/modulo_arithmetic_integral_const.rs:12:5 | LL | 1 % -2; | ^^^^^^ @@ -18,7 +18,7 @@ LL | 1 % -2; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 3` - --> $DIR/modulo_arithmetic_integral_const.rs:8:5 + --> $DIR/modulo_arithmetic_integral_const.rs:13:5 | LL | (1 - 2) % (1 + 2); | ^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | (1 - 2) % (1 + 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `3 % -1` - --> $DIR/modulo_arithmetic_integral_const.rs:9:5 + --> $DIR/modulo_arithmetic_integral_const.rs:14:5 | LL | (1 + 2) % (1 - 2); | ^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | (1 + 2) % (1 - 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-35 % 300000` - --> $DIR/modulo_arithmetic_integral_const.rs:10:5 + --> $DIR/modulo_arithmetic_integral_const.rs:15:5 | LL | 35 * (7 - 4 * 2) % (-500 * -600); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | 35 * (7 - 4 * 2) % (-500 * -600); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:12:5 + --> $DIR/modulo_arithmetic_integral_const.rs:17:5 | LL | -1i8 % 2i8; | ^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | -1i8 % 2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:13:5 + --> $DIR/modulo_arithmetic_integral_const.rs:18:5 | LL | 1i8 % -2i8; | ^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | 1i8 % -2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:14:5 + --> $DIR/modulo_arithmetic_integral_const.rs:19:5 | LL | -1i16 % 2i16; | ^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | -1i16 % 2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:15:5 + --> $DIR/modulo_arithmetic_integral_const.rs:20:5 | LL | 1i16 % -2i16; | ^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL | 1i16 % -2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:16:5 + --> $DIR/modulo_arithmetic_integral_const.rs:21:5 | LL | -1i32 % 2i32; | ^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | -1i32 % 2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:17:5 + --> $DIR/modulo_arithmetic_integral_const.rs:22:5 | LL | 1i32 % -2i32; | ^^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL | 1i32 % -2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:18:5 + --> $DIR/modulo_arithmetic_integral_const.rs:23:5 | LL | -1i64 % 2i64; | ^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL | -1i64 % 2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:19:5 + --> $DIR/modulo_arithmetic_integral_const.rs:24:5 | LL | 1i64 % -2i64; | ^^^^^^^^^^^^ @@ -117,7 +117,7 @@ LL | 1i64 % -2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:20:5 + --> $DIR/modulo_arithmetic_integral_const.rs:25:5 | LL | -1i128 % 2i128; | ^^^^^^^^^^^^^^ @@ -126,7 +126,7 @@ LL | -1i128 % 2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:21:5 + --> $DIR/modulo_arithmetic_integral_const.rs:26:5 | LL | 1i128 % -2i128; | ^^^^^^^^^^^^^^ @@ -135,7 +135,7 @@ LL | 1i128 % -2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:22:5 + --> $DIR/modulo_arithmetic_integral_const.rs:27:5 | LL | -1isize % 2isize; | ^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | -1isize % 2isize; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:23:5 + --> $DIR/modulo_arithmetic_integral_const.rs:28:5 | LL | 1isize % -2isize; | ^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs b/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs index ad0d694a2174a..02b43cce2bd4c 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.rs @@ -14,7 +14,7 @@ mod issue_6089 { fn test(self: &Self); } - struct S1 {} + struct S1; impl T1 for S1 { fn test(self: &Self) {} @@ -32,7 +32,7 @@ mod issue_6089 { fn call_with_mut_self(&mut self); } - struct S2 {} + struct S2; // The method's signature will be expanded to: // fn call_with_mut_self<'life0>(self: &'life0 mut Self) {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index f3eafe8e9279e..1456204ca8692 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -268,7 +268,7 @@ mod issue4291 { mod issue2944 { trait Foo {} - struct Bar {} + struct Bar; struct Baz<'a> { bar: &'a Bar, } diff --git a/src/tools/clippy/tests/ui/needless_match.fixed b/src/tools/clippy/tests/ui/needless_match.fixed index ece18ad737fda..9ccccaa1725a6 100644 --- a/src/tools/clippy/tests/ui/needless_match.fixed +++ b/src/tools/clippy/tests/ui/needless_match.fixed @@ -4,38 +4,35 @@ #![allow(dead_code)] #[derive(Clone, Copy)] -enum Choice { +enum Simple { A, B, C, D, } -#[allow(unused_mut)] fn useless_match() { - let mut i = 10; + let i = 10; let _: i32 = i; - let _: i32 = i; - let mut _i_mut = i; - let s = "test"; let _: &str = s; } -fn custom_type_match(se: Choice) { - let _: Choice = se; +fn custom_type_match() { + let se = Simple::A; + let _: Simple = se; // Don't trigger - let _: Choice = match se { - Choice::A => Choice::A, - Choice::B => Choice::B, - _ => Choice::C, + let _: Simple = match se { + Simple::A => Simple::A, + Simple::B => Simple::B, + _ => Simple::C, }; // Mingled, don't trigger - let _: Choice = match se { - Choice::A => Choice::B, - Choice::B => Choice::C, - Choice::C => Choice::D, - Choice::D => Choice::A, + let _: Simple = match se { + Simple::A => Simple::B, + Simple::B => Simple::C, + Simple::C => Simple::D, + Simple::D => Simple::A, }; } @@ -55,29 +52,146 @@ fn func_ret_err(err: T) -> Result { fn result_match() { let _: Result = Ok(1); let _: Result = func_ret_err(0_i32); + // as ref, don't trigger + let res = &func_ret_err(0_i32); + let _: Result<&i32, &i32> = match *res { + Ok(ref x) => Ok(x), + Err(ref x) => Err(x), + }; } -fn if_let_option() -> Option { - Some(1) +fn if_let_option() { + let _ = Some(1); + + fn do_something() {} + + // Don't trigger + let _ = if let Some(a) = Some(1) { + Some(a) + } else { + do_something(); + None + }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { + do_something(); + Some(a) + } else { + None + }; } -fn if_let_result(x: Result<(), i32>) { - let _: Result<(), i32> = x; - let _: Result<(), i32> = x; +fn if_let_result() { + let x: Result = Ok(1); + let _: Result = x; + let _: Result = x; // Input type mismatch, don't trigger - let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x }; + let _: Result = if let Err(e) = Ok(1) { Err(e) } else { x }; } -fn if_let_custom_enum(x: Choice) { - let _: Choice = x; +fn if_let_custom_enum(x: Simple) { + let _: Simple = x; + // Don't trigger - let _: Choice = if let Choice::A = x { - Choice::A + let _: Simple = if let Simple::A = x { + Simple::A } else if true { - Choice::B + Simple::B } else { x }; } +mod issue8542 { + #[derive(Clone, Copy)] + enum E { + VariantA(u8, u8), + VariantB(u8, bool), + } + + enum Complex { + A(u8), + B(u8, bool), + C(u8, i32, f64), + D(E, bool), + } + + fn match_test() { + let ce = Complex::B(8, false); + let aa = 0_u8; + let bb = false; + + let _: Complex = ce; + + // Don't trigger + let _: Complex = match ce { + Complex::A(_) => Complex::A(aa), + Complex::B(_, b) => Complex::B(aa, b), + Complex::C(_, b, _) => Complex::C(aa, b, 64_f64), + Complex::D(e, b) => Complex::D(e, b), + }; + + // Don't trigger + let _: Complex = match ce { + Complex::A(a) => Complex::A(a), + Complex::B(a, _) => Complex::B(a, bb), + Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64), + _ => ce, + }; + } +} + +/// Lint triggered when type coercions happen. +/// Do NOT trigger on any of these. +mod issue8551 { + trait Trait {} + struct Struct; + impl Trait for Struct {} + + fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> { + match s { + Some(s) => Some(s), + None => None, + } + } + + fn lint_tests() { + let option: Option<&Struct> = None; + let _: Option<&dyn Trait> = match option { + Some(s) => Some(s), + None => None, + }; + + let _: Option<&dyn Trait> = if true { + match option { + Some(s) => Some(s), + None => None, + } + } else { + None + }; + + let result: Result<&Struct, i32> = Err(0); + let _: Result<&dyn Trait, i32> = match result { + Ok(s) => Ok(s), + Err(e) => Err(e), + }; + + let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None }; + } +} + +trait Tr { + fn as_mut(&mut self) -> Result<&mut i32, &mut i32>; +} +impl Tr for Result { + fn as_mut(&mut self) -> Result<&mut i32, &mut i32> { + match self { + Ok(x) => Ok(x), + Err(e) => Err(e), + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.rs b/src/tools/clippy/tests/ui/needless_match.rs index 36649de35a603..d210ecff7f163 100644 --- a/src/tools/clippy/tests/ui/needless_match.rs +++ b/src/tools/clippy/tests/ui/needless_match.rs @@ -4,33 +4,21 @@ #![allow(dead_code)] #[derive(Clone, Copy)] -enum Choice { +enum Simple { A, B, C, D, } -#[allow(unused_mut)] fn useless_match() { - let mut i = 10; + let i = 10; let _: i32 = match i { 0 => 0, 1 => 1, 2 => 2, _ => i, }; - let _: i32 = match i { - 0 => 0, - 1 => 1, - ref i => *i, - }; - let mut _i_mut = match i { - 0 => 0, - 1 => 1, - ref mut i => *i, - }; - let s = "test"; let _: &str = match s { "a" => "a", @@ -39,25 +27,26 @@ fn useless_match() { }; } -fn custom_type_match(se: Choice) { - let _: Choice = match se { - Choice::A => Choice::A, - Choice::B => Choice::B, - Choice::C => Choice::C, - Choice::D => Choice::D, +fn custom_type_match() { + let se = Simple::A; + let _: Simple = match se { + Simple::A => Simple::A, + Simple::B => Simple::B, + Simple::C => Simple::C, + Simple::D => Simple::D, }; // Don't trigger - let _: Choice = match se { - Choice::A => Choice::A, - Choice::B => Choice::B, - _ => Choice::C, + let _: Simple = match se { + Simple::A => Simple::A, + Simple::B => Simple::B, + _ => Simple::C, }; // Mingled, don't trigger - let _: Choice = match se { - Choice::A => Choice::B, - Choice::B => Choice::C, - Choice::C => Choice::D, - Choice::D => Choice::A, + let _: Simple = match se { + Simple::A => Simple::B, + Simple::B => Simple::C, + Simple::C => Simple::D, + Simple::D => Simple::A, }; } @@ -86,37 +75,160 @@ fn result_match() { Err(err) => Err(err), Ok(a) => Ok(a), }; + // as ref, don't trigger + let res = &func_ret_err(0_i32); + let _: Result<&i32, &i32> = match *res { + Ok(ref x) => Ok(x), + Err(ref x) => Err(x), + }; } -fn if_let_option() -> Option { - if let Some(a) = Some(1) { Some(a) } else { None } +fn if_let_option() { + let _ = if let Some(a) = Some(1) { Some(a) } else { None }; + + fn do_something() {} + + // Don't trigger + let _ = if let Some(a) = Some(1) { + Some(a) + } else { + do_something(); + None + }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { + do_something(); + Some(a) + } else { + None + }; } -fn if_let_result(x: Result<(), i32>) { - let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x }; - let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x }; +fn if_let_result() { + let x: Result = Ok(1); + let _: Result = if let Err(e) = x { Err(e) } else { x }; + let _: Result = if let Ok(val) = x { Ok(val) } else { x }; // Input type mismatch, don't trigger - let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x }; + let _: Result = if let Err(e) = Ok(1) { Err(e) } else { x }; } -fn if_let_custom_enum(x: Choice) { - let _: Choice = if let Choice::A = x { - Choice::A - } else if let Choice::B = x { - Choice::B - } else if let Choice::C = x { - Choice::C +fn if_let_custom_enum(x: Simple) { + let _: Simple = if let Simple::A = x { + Simple::A + } else if let Simple::B = x { + Simple::B + } else if let Simple::C = x { + Simple::C } else { x }; + // Don't trigger - let _: Choice = if let Choice::A = x { - Choice::A + let _: Simple = if let Simple::A = x { + Simple::A } else if true { - Choice::B + Simple::B } else { x }; } +mod issue8542 { + #[derive(Clone, Copy)] + enum E { + VariantA(u8, u8), + VariantB(u8, bool), + } + + enum Complex { + A(u8), + B(u8, bool), + C(u8, i32, f64), + D(E, bool), + } + + fn match_test() { + let ce = Complex::B(8, false); + let aa = 0_u8; + let bb = false; + + let _: Complex = match ce { + Complex::A(a) => Complex::A(a), + Complex::B(a, b) => Complex::B(a, b), + Complex::C(a, b, c) => Complex::C(a, b, c), + Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b), + Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b), + }; + + // Don't trigger + let _: Complex = match ce { + Complex::A(_) => Complex::A(aa), + Complex::B(_, b) => Complex::B(aa, b), + Complex::C(_, b, _) => Complex::C(aa, b, 64_f64), + Complex::D(e, b) => Complex::D(e, b), + }; + + // Don't trigger + let _: Complex = match ce { + Complex::A(a) => Complex::A(a), + Complex::B(a, _) => Complex::B(a, bb), + Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64), + _ => ce, + }; + } +} + +/// Lint triggered when type coercions happen. +/// Do NOT trigger on any of these. +mod issue8551 { + trait Trait {} + struct Struct; + impl Trait for Struct {} + + fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> { + match s { + Some(s) => Some(s), + None => None, + } + } + + fn lint_tests() { + let option: Option<&Struct> = None; + let _: Option<&dyn Trait> = match option { + Some(s) => Some(s), + None => None, + }; + + let _: Option<&dyn Trait> = if true { + match option { + Some(s) => Some(s), + None => None, + } + } else { + None + }; + + let result: Result<&Struct, i32> = Err(0); + let _: Result<&dyn Trait, i32> = match result { + Ok(s) => Ok(s), + Err(e) => Err(e), + }; + + let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None }; + } +} + +trait Tr { + fn as_mut(&mut self) -> Result<&mut i32, &mut i32>; +} +impl Tr for Result { + fn as_mut(&mut self) -> Result<&mut i32, &mut i32> { + match self { + Ok(x) => Ok(x), + Err(e) => Err(e), + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.stderr b/src/tools/clippy/tests/ui/needless_match.stderr index ad1525406ade7..34c5226f06057 100644 --- a/src/tools/clippy/tests/ui/needless_match.stderr +++ b/src/tools/clippy/tests/ui/needless_match.stderr @@ -1,5 +1,5 @@ error: this match expression is unnecessary - --> $DIR/needless_match.rs:17:18 + --> $DIR/needless_match.rs:16:18 | LL | let _: i32 = match i { | __________________^ @@ -13,29 +13,7 @@ LL | | }; = note: `-D clippy::needless-match` implied by `-D warnings` error: this match expression is unnecessary - --> $DIR/needless_match.rs:23:18 - | -LL | let _: i32 = match i { - | __________________^ -LL | | 0 => 0, -LL | | 1 => 1, -LL | | ref i => *i, -LL | | }; - | |_____^ help: replace it with: `i` - -error: this match expression is unnecessary - --> $DIR/needless_match.rs:28:22 - | -LL | let mut _i_mut = match i { - | ______________________^ -LL | | 0 => 0, -LL | | 1 => 1, -LL | | ref mut i => *i, -LL | | }; - | |_____^ help: replace it with: `i` - -error: this match expression is unnecessary - --> $DIR/needless_match.rs:35:19 + --> $DIR/needless_match.rs:23:19 | LL | let _: &str = match s { | ___________________^ @@ -46,19 +24,19 @@ LL | | }; | |_____^ help: replace it with: `s` error: this match expression is unnecessary - --> $DIR/needless_match.rs:43:21 + --> $DIR/needless_match.rs:32:21 | -LL | let _: Choice = match se { +LL | let _: Simple = match se { | _____________________^ -LL | | Choice::A => Choice::A, -LL | | Choice::B => Choice::B, -LL | | Choice::C => Choice::C, -LL | | Choice::D => Choice::D, +LL | | Simple::A => Simple::A, +LL | | Simple::B => Simple::B, +LL | | Simple::C => Simple::C, +LL | | Simple::D => Simple::D, LL | | }; | |_____^ help: replace it with: `se` error: this match expression is unnecessary - --> $DIR/needless_match.rs:65:26 + --> $DIR/needless_match.rs:54:26 | LL | let _: Option = match x { | __________________________^ @@ -68,7 +46,7 @@ LL | | }; | |_____^ help: replace it with: `x` error: this match expression is unnecessary - --> $DIR/needless_match.rs:81:31 + --> $DIR/needless_match.rs:70:31 | LL | let _: Result = match Ok(1) { | _______________________________^ @@ -78,7 +56,7 @@ LL | | }; | |_____^ help: replace it with: `Ok(1)` error: this match expression is unnecessary - --> $DIR/needless_match.rs:85:31 + --> $DIR/needless_match.rs:74:31 | LL | let _: Result = match func_ret_err(0_i32) { | _______________________________^ @@ -88,35 +66,48 @@ LL | | }; | |_____^ help: replace it with: `func_ret_err(0_i32)` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:92:5 + --> $DIR/needless_match.rs:87:13 | -LL | if let Some(a) = Some(1) { Some(a) } else { None } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)` +LL | let _ = if let Some(a) = Some(1) { Some(a) } else { None }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:96:30 + --> $DIR/needless_match.rs:110:31 | -LL | let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` +LL | let _: Result = if let Err(e) = x { Err(e) } else { x }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:97:30 + --> $DIR/needless_match.rs:111:31 | -LL | let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` +LL | let _: Result = if let Ok(val) = x { Ok(val) } else { x }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:103:21 + --> $DIR/needless_match.rs:117:21 | -LL | let _: Choice = if let Choice::A = x { +LL | let _: Simple = if let Simple::A = x { | _____________________^ -LL | | Choice::A -LL | | } else if let Choice::B = x { -LL | | Choice::B +LL | | Simple::A +LL | | } else if let Simple::B = x { +LL | | Simple::B ... | LL | | x LL | | }; | |_____^ help: replace it with: `x` -error: aborting due to 12 previous errors +error: this match expression is unnecessary + --> $DIR/needless_match.rs:156:26 + | +LL | let _: Complex = match ce { + | __________________________^ +LL | | Complex::A(a) => Complex::A(a), +LL | | Complex::B(a, b) => Complex::B(a, b), +LL | | Complex::C(a, b, c) => Complex::C(a, b, c), +LL | | Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b), +LL | | Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b), +LL | | }; + | |_________^ help: replace it with: `ce` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.fixed b/src/tools/clippy/tests/ui/needless_option_as_deref.fixed index d721452ae8880..c09b07db3dca9 100644 --- a/src/tools/clippy/tests/ui/needless_option_as_deref.fixed +++ b/src/tools/clippy/tests/ui/needless_option_as_deref.fixed @@ -1,13 +1,41 @@ // run-rustfix -#[warn(clippy::needless_option_as_deref)] +#![allow(unused)] +#![warn(clippy::needless_option_as_deref)] fn main() { // should lint let _: Option<&usize> = Some(&1); let _: Option<&mut usize> = Some(&mut 1); + let mut y = 0; + let mut x = Some(&mut y); + let _ = x; + // should not lint let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + + // #7846 + let mut i = 0; + let mut opt_vec = vec![Some(&mut i)]; + opt_vec[0].as_deref_mut().unwrap(); + + let mut i = 0; + let x = &mut Some(&mut i); + (*x).as_deref_mut(); + + // #8047 + let mut y = 0; + let mut x = Some(&mut y); + x.as_deref_mut(); + dbg!(x); +} + +struct S<'a> { + opt: Option<&'a mut usize>, +} + +fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> { + s.opt.as_deref_mut() } diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.rs b/src/tools/clippy/tests/ui/needless_option_as_deref.rs index bb15512adf6aa..c3ba27ecccf22 100644 --- a/src/tools/clippy/tests/ui/needless_option_as_deref.rs +++ b/src/tools/clippy/tests/ui/needless_option_as_deref.rs @@ -1,13 +1,41 @@ // run-rustfix -#[warn(clippy::needless_option_as_deref)] +#![allow(unused)] +#![warn(clippy::needless_option_as_deref)] fn main() { // should lint let _: Option<&usize> = Some(&1).as_deref(); let _: Option<&mut usize> = Some(&mut 1).as_deref_mut(); + let mut y = 0; + let mut x = Some(&mut y); + let _ = x.as_deref_mut(); + // should not lint let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + + // #7846 + let mut i = 0; + let mut opt_vec = vec![Some(&mut i)]; + opt_vec[0].as_deref_mut().unwrap(); + + let mut i = 0; + let x = &mut Some(&mut i); + (*x).as_deref_mut(); + + // #8047 + let mut y = 0; + let mut x = Some(&mut y); + x.as_deref_mut(); + dbg!(x); +} + +struct S<'a> { + opt: Option<&'a mut usize>, +} + +fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> { + s.opt.as_deref_mut() } diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.stderr b/src/tools/clippy/tests/ui/needless_option_as_deref.stderr index 5dd507b4a71e8..bc07db5b38ed3 100644 --- a/src/tools/clippy/tests/ui/needless_option_as_deref.stderr +++ b/src/tools/clippy/tests/ui/needless_option_as_deref.stderr @@ -1,5 +1,5 @@ error: derefed type is same as origin - --> $DIR/needless_option_as_deref.rs:7:29 + --> $DIR/needless_option_as_deref.rs:8:29 | LL | let _: Option<&usize> = Some(&1).as_deref(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)` @@ -7,10 +7,16 @@ LL | let _: Option<&usize> = Some(&1).as_deref(); = note: `-D clippy::needless-option-as-deref` implied by `-D warnings` error: derefed type is same as origin - --> $DIR/needless_option_as_deref.rs:8:33 + --> $DIR/needless_option_as_deref.rs:9:33 | LL | let _: Option<&mut usize> = Some(&mut 1).as_deref_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)` -error: aborting due to 2 previous errors +error: derefed type is same as origin + --> $DIR/needless_option_as_deref.rs:13:13 + | +LL | let _ = x.as_deref_mut(); + | ^^^^^^^^^^^^^^^^ help: try this: `x` + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs index 5427c88faf348..7ece66a1ccb6f 100644 --- a/src/tools/clippy/tests/ui/no_effect.rs +++ b/src/tools/clippy/tests/ui/no_effect.rs @@ -78,7 +78,7 @@ impl FnOnce<(&str,)> for GreetStruct2 { } } -struct GreetStruct3 {} +struct GreetStruct3; impl FnOnce<(&str,)> for GreetStruct3 { type Output = (); diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed index 7d29445e66c8b..1290bd8efebd2 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed @@ -80,6 +80,9 @@ fn option_map_unit_fn() { if let Some(ref value) = x.field { do_nothing(value + captured) } - if let Some(a) = option() { do_nothing(a) }} + if let Some(a) = option() { do_nothing(a) } + + if let Some(value) = option() { println!("{:?}", value) } +} fn main() {} diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs index b6f834f686f9f..f3e5b62c65b7f 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs @@ -80,6 +80,9 @@ fn option_map_unit_fn() { x.field.map(|ref value| { do_nothing(value + captured) }); - option().map(do_nothing);} + option().map(do_nothing); + + option().map(|value| println!("{:?}", value)); +} fn main() {} diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr index 8abdbcafb6e93..ab2a294a060f0 100644 --- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr @@ -139,10 +139,18 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:83:5 | -LL | option().map(do_nothing);} +LL | option().map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^^- | | | help: try this: `if let Some(a) = option() { do_nothing(a) }` -error: aborting due to 18 previous errors +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> $DIR/option_map_unit_fn_fixable.rs:85:5 + | +LL | option().map(|value| println!("{:?}", value)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = option() { println!("{:?}", value) }` + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.fixed b/src/tools/clippy/tests/ui/or_then_unwrap.fixed index 27d4b795a5eeb..6e0d5a87f6807 100644 --- a/src/tools/clippy/tests/ui/or_then_unwrap.fixed +++ b/src/tools/clippy/tests/ui/or_then_unwrap.fixed @@ -3,7 +3,7 @@ #![warn(clippy::or_then_unwrap)] #![allow(clippy::map_identity)] -struct SomeStruct {} +struct SomeStruct; impl SomeStruct { fn or(self, _: Option) -> Self { self @@ -11,7 +11,7 @@ impl SomeStruct { fn unwrap(&self) {} } -struct SomeOtherStruct {} +struct SomeOtherStruct; impl SomeOtherStruct { fn or(self) -> Self { self diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.rs b/src/tools/clippy/tests/ui/or_then_unwrap.rs index 0dab5ae2f1c04..e406a71d46d00 100644 --- a/src/tools/clippy/tests/ui/or_then_unwrap.rs +++ b/src/tools/clippy/tests/ui/or_then_unwrap.rs @@ -3,7 +3,7 @@ #![warn(clippy::or_then_unwrap)] #![allow(clippy::map_identity)] -struct SomeStruct {} +struct SomeStruct; impl SomeStruct { fn or(self, _: Option) -> Self { self @@ -11,7 +11,7 @@ impl SomeStruct { fn unwrap(&self) {} } -struct SomeOtherStruct {} +struct SomeOtherStruct; impl SomeOtherStruct { fn or(self) -> Self { self diff --git a/src/tools/clippy/tests/ui/panicking_macros.rs b/src/tools/clippy/tests/ui/panicking_macros.rs index 7338064646244..12a0c776ae2fd 100644 --- a/src/tools/clippy/tests/ui/panicking_macros.rs +++ b/src/tools/clippy/tests/ui/panicking_macros.rs @@ -1,8 +1,23 @@ -#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] #![allow(clippy::assertions_on_constants, clippy::eq_op)] +#![feature(inline_const)] +#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] extern crate core; +const _: () = { + if 1 == 0 { + panic!("A balanced diet means a cupcake in each hand"); + } +}; + +fn inline_const() { + let _ = const { + if 1 == 0 { + panic!("When nothing goes right, go left") + } + }; +} + fn panic() { let a = 2; panic!(); diff --git a/src/tools/clippy/tests/ui/panicking_macros.stderr b/src/tools/clippy/tests/ui/panicking_macros.stderr index bfd1c7a380149..4ceb6d1440f61 100644 --- a/src/tools/clippy/tests/ui/panicking_macros.stderr +++ b/src/tools/clippy/tests/ui/panicking_macros.stderr @@ -1,5 +1,5 @@ error: `panic` should not be present in production code - --> $DIR/panicking_macros.rs:8:5 + --> $DIR/panicking_macros.rs:23:5 | LL | panic!(); | ^^^^^^^^ @@ -7,19 +7,19 @@ LL | panic!(); = note: `-D clippy::panic` implied by `-D warnings` error: `panic` should not be present in production code - --> $DIR/panicking_macros.rs:9:5 + --> $DIR/panicking_macros.rs:24:5 | LL | panic!("message"); | ^^^^^^^^^^^^^^^^^ error: `panic` should not be present in production code - --> $DIR/panicking_macros.rs:10:5 + --> $DIR/panicking_macros.rs:25:5 | LL | panic!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:16:5 + --> $DIR/panicking_macros.rs:31:5 | LL | todo!(); | ^^^^^^^ @@ -27,19 +27,19 @@ LL | todo!(); = note: `-D clippy::todo` implied by `-D warnings` error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:17:5 + --> $DIR/panicking_macros.rs:32:5 | LL | todo!("message"); | ^^^^^^^^^^^^^^^^ error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:18:5 + --> $DIR/panicking_macros.rs:33:5 | LL | todo!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:24:5 + --> $DIR/panicking_macros.rs:39:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ @@ -47,19 +47,19 @@ LL | unimplemented!(); = note: `-D clippy::unimplemented` implied by `-D warnings` error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:25:5 + --> $DIR/panicking_macros.rs:40:5 | LL | unimplemented!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:26:5 + --> $DIR/panicking_macros.rs:41:5 | LL | unimplemented!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> $DIR/panicking_macros.rs:32:5 + --> $DIR/panicking_macros.rs:47:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ @@ -67,37 +67,37 @@ LL | unreachable!(); = note: `-D clippy::unreachable` implied by `-D warnings` error: usage of the `unreachable!` macro - --> $DIR/panicking_macros.rs:33:5 + --> $DIR/panicking_macros.rs:48:5 | LL | unreachable!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> $DIR/panicking_macros.rs:34:5 + --> $DIR/panicking_macros.rs:49:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `panic` should not be present in production code - --> $DIR/panicking_macros.rs:40:5 + --> $DIR/panicking_macros.rs:55:5 | LL | panic!(); | ^^^^^^^^ error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:41:5 + --> $DIR/panicking_macros.rs:56:5 | LL | todo!(); | ^^^^^^^ error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:42:5 + --> $DIR/panicking_macros.rs:57:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> $DIR/panicking_macros.rs:43:5 + --> $DIR/panicking_macros.rs:58:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs index 03dd938a2339e..814bbc7af713b 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.rs +++ b/src/tools/clippy/tests/ui/ptr_arg.rs @@ -112,7 +112,7 @@ mod issue_5644 { ) { } - struct S {} + struct S; impl S { fn allowed( #[allow(clippy::ptr_arg)] _v: &Vec, diff --git a/src/tools/clippy/tests/ui/recursive_format_impl.rs b/src/tools/clippy/tests/ui/recursive_format_impl.rs index 9241bf7ed7402..f72fc77ab9977 100644 --- a/src/tools/clippy/tests/ui/recursive_format_impl.rs +++ b/src/tools/clippy/tests/ui/recursive_format_impl.rs @@ -66,7 +66,7 @@ impl std::fmt::Display for D { // Check for use of self as Display, in Display impl // Triggers on direct use of self -struct G {} +struct G; impl std::fmt::Display for G { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -75,7 +75,7 @@ impl std::fmt::Display for G { } // Triggers on reference to self -struct H {} +struct H; impl std::fmt::Display for H { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -90,7 +90,7 @@ impl std::fmt::Debug for H { } // Triggers on multiple reference to self -struct H2 {} +struct H2; impl std::fmt::Display for H2 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -99,7 +99,7 @@ impl std::fmt::Display for H2 { } // Doesn't trigger on correct deref -struct I {} +struct I; impl std::ops::Deref for I { type Target = str; @@ -122,7 +122,7 @@ impl std::fmt::Debug for I { } // Doesn't trigger on multiple correct deref -struct I2 {} +struct I2; impl std::ops::Deref for I2 { type Target = str; @@ -139,7 +139,7 @@ impl std::fmt::Display for I2 { } // Doesn't trigger on multiple correct deref -struct I3 {} +struct I3; impl std::ops::Deref for I3 { type Target = str; @@ -156,7 +156,7 @@ impl std::fmt::Display for I3 { } // Does trigger when deref resolves to self -struct J {} +struct J; impl std::ops::Deref for J { type Target = str; @@ -178,7 +178,7 @@ impl std::fmt::Debug for J { } } -struct J2 {} +struct J2; impl std::ops::Deref for J2 { type Target = str; @@ -194,7 +194,7 @@ impl std::fmt::Display for J2 { } } -struct J3 {} +struct J3; impl std::ops::Deref for J3 { type Target = str; @@ -210,7 +210,7 @@ impl std::fmt::Display for J3 { } } -struct J4 {} +struct J4; impl std::ops::Deref for J4 { type Target = str; @@ -227,7 +227,7 @@ impl std::fmt::Display for J4 { } // Doesn't trigger on Debug from Display -struct K {} +struct K; impl std::fmt::Debug for K { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -242,7 +242,7 @@ impl std::fmt::Display for K { } // Doesn't trigger on Display from Debug -struct K2 {} +struct K2; impl std::fmt::Debug for K2 { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { diff --git a/src/tools/clippy/tests/ui/redundant_allocation.rs b/src/tools/clippy/tests/ui/redundant_allocation.rs index 52fbc91e32555..80f94e5f3cbbb 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation.rs +++ b/src/tools/clippy/tests/ui/redundant_allocation.rs @@ -3,7 +3,7 @@ #![allow(clippy::blacklisted_name, unused_variables, dead_code)] #![allow(unused_imports)] -pub struct MyStruct {} +pub struct MyStruct; pub struct SubT { foo: T, diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed b/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed index ef089b25f47fd..e7ed84731c02e 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed +++ b/src/tools/clippy/tests/ui/redundant_allocation_fixable.fixed @@ -4,7 +4,7 @@ #![allow(clippy::blacklisted_name, unused_variables, dead_code)] #![allow(unused_imports)] -pub struct MyStruct {} +pub struct MyStruct; pub struct SubT { foo: T, diff --git a/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs b/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs index fefa87721d720..de763f98b5c89 100644 --- a/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs +++ b/src/tools/clippy/tests/ui/redundant_allocation_fixable.rs @@ -4,7 +4,7 @@ #![allow(clippy::blacklisted_name, unused_variables, dead_code)] #![allow(unused_imports)] -pub struct MyStruct {} +pub struct MyStruct; pub struct SubT { foo: T, diff --git a/src/tools/clippy/tests/ui/redundant_clone.fixed b/src/tools/clippy/tests/ui/redundant_clone.fixed index 16b40dcd90286..1525f6a93dfdd 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.fixed +++ b/src/tools/clippy/tests/ui/redundant_clone.fixed @@ -1,7 +1,7 @@ // run-rustfix // rustfix-only-machine-applicable -#![allow(clippy::implicit_clone)] +#![allow(clippy::implicit_clone, clippy::drop_non_drop)] use std::ffi::OsString; use std::path::Path; diff --git a/src/tools/clippy/tests/ui/redundant_clone.rs b/src/tools/clippy/tests/ui/redundant_clone.rs index bd3d7365229fb..2f82aefd92830 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.rs +++ b/src/tools/clippy/tests/ui/redundant_clone.rs @@ -1,7 +1,7 @@ // run-rustfix // rustfix-only-machine-applicable -#![allow(clippy::implicit_clone)] +#![allow(clippy::implicit_clone, clippy::drop_non_drop)] use std::ffi::OsString; use std::path::Path; diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed index 921249606ad27..acc8f1e25b6ed 100644 --- a/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed @@ -3,7 +3,7 @@ #![allow(unused)] #[derive(Debug)] -struct Foo {} +struct Foo; const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static. diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs index 4d4b249d076ff..f2f0f78659c93 100644 --- a/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs @@ -3,7 +3,7 @@ #![allow(unused)] #[derive(Debug)] -struct Foo {} +struct Foo; const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed index 631042c616bc0..14c331f67e739 100644 --- a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed @@ -75,6 +75,8 @@ fn result_map_unit_fn() { if let Ok(ref value) = x.field { do_nothing(value + captured) } + + if let Ok(value) = x.field { println!("{:?}", value) } } fn main() {} diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs index 679eb2326268c..8b0fca9ece1a3 100644 --- a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs @@ -75,6 +75,8 @@ fn result_map_unit_fn() { x.field.map(|ref value| { do_nothing(value + captured) }); + + x.field.map(|value| println!("{:?}", value)); } fn main() {} diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr index 4f3a8c6b79239..782febd526441 100644 --- a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr @@ -136,5 +136,13 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | | | help: try this: `if let Ok(ref value) = x.field { do_nothing(value + captured) }` -error: aborting due to 17 previous errors +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` + --> $DIR/result_map_unit_fn_fixable.rs:79:5 + | +LL | x.field.map(|value| println!("{:?}", value)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { println!("{:?}", value) }` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/same_item_push.rs b/src/tools/clippy/tests/ui/same_item_push.rs index 9d420ec672a26..99964f0de075c 100644 --- a/src/tools/clippy/tests/ui/same_item_push.rs +++ b/src/tools/clippy/tests/ui/same_item_push.rs @@ -120,7 +120,7 @@ fn main() { } // Fix #5979 #[derive(Clone)] - struct S {} + struct S; trait T {} impl T for S {} diff --git a/src/tools/clippy/tests/ui/single_element_loop.fixed b/src/tools/clippy/tests/ui/single_element_loop.fixed index c307afffcb863..63d31ff83f9b5 100644 --- a/src/tools/clippy/tests/ui/single_element_loop.fixed +++ b/src/tools/clippy/tests/ui/single_element_loop.fixed @@ -6,11 +6,31 @@ fn main() { let item1 = 2; { let item = &item1; - println!("{}", item); + dbg!(item); } { let item = &item1; - println!("{:?}", item); + dbg!(item); + } + + { + let item = &(0..5); + dbg!(item); + } + + { + let item = &mut (0..5); + dbg!(item); + } + + { + let item = 0..5; + dbg!(item); + } + + { + let item = 0..5; + dbg!(item); } } diff --git a/src/tools/clippy/tests/ui/single_element_loop.rs b/src/tools/clippy/tests/ui/single_element_loop.rs index 2c0c03b721199..2cda5a329d254 100644 --- a/src/tools/clippy/tests/ui/single_element_loop.rs +++ b/src/tools/clippy/tests/ui/single_element_loop.rs @@ -5,10 +5,26 @@ fn main() { let item1 = 2; for item in &[item1] { - println!("{}", item); + dbg!(item); } for item in [item1].iter() { - println!("{:?}", item); + dbg!(item); + } + + for item in &[0..5] { + dbg!(item); + } + + for item in [0..5].iter_mut() { + dbg!(item); + } + + for item in [0..5] { + dbg!(item); + } + + for item in [0..5].into_iter() { + dbg!(item); } } diff --git a/src/tools/clippy/tests/ui/single_element_loop.stderr b/src/tools/clippy/tests/ui/single_element_loop.stderr index f52ca8c5a9b0c..0aeb8da1a2e23 100644 --- a/src/tools/clippy/tests/ui/single_element_loop.stderr +++ b/src/tools/clippy/tests/ui/single_element_loop.stderr @@ -2,7 +2,7 @@ error: for loop over a single element --> $DIR/single_element_loop.rs:7:5 | LL | / for item in &[item1] { -LL | | println!("{}", item); +LL | | dbg!(item); LL | | } | |_____^ | @@ -11,7 +11,7 @@ help: try | LL ~ { LL + let item = &item1; -LL + println!("{}", item); +LL + dbg!(item); LL + } | @@ -19,7 +19,7 @@ error: for loop over a single element --> $DIR/single_element_loop.rs:11:5 | LL | / for item in [item1].iter() { -LL | | println!("{:?}", item); +LL | | dbg!(item); LL | | } | |_____^ | @@ -27,9 +27,73 @@ help: try | LL ~ { LL + let item = &item1; -LL + println!("{:?}", item); +LL + dbg!(item); LL + } | -error: aborting due to 2 previous errors +error: for loop over a single element + --> $DIR/single_element_loop.rs:15:5 + | +LL | / for item in &[0..5] { +LL | | dbg!(item); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let item = &(0..5); +LL + dbg!(item); +LL + } + | + +error: for loop over a single element + --> $DIR/single_element_loop.rs:19:5 + | +LL | / for item in [0..5].iter_mut() { +LL | | dbg!(item); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let item = &mut (0..5); +LL + dbg!(item); +LL + } + | + +error: for loop over a single element + --> $DIR/single_element_loop.rs:23:5 + | +LL | / for item in [0..5] { +LL | | dbg!(item); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let item = 0..5; +LL + dbg!(item); +LL + } + | + +error: for loop over a single element + --> $DIR/single_element_loop.rs:27:5 + | +LL | / for item in [0..5].into_iter() { +LL | | dbg!(item); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let item = 0..5; +LL + dbg!(item); +LL + } + | + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs index 21de19a260147..f5ca91143af25 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs @@ -62,7 +62,7 @@ trait BadTrait: Default + Clone { } #[derive(Default, Clone)] -struct Life {} +struct Life; impl T for Life { // this should not warn @@ -85,7 +85,7 @@ trait Iter: Iterator { } } -struct Foo {} +struct Foo; trait FooIter: Iterator { fn bar() diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index 9b681a79aae7a..5b688ce4d644f 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -73,6 +73,10 @@ fn crosspointer() { fn int_to_char() { let _: char = unsafe { std::mem::transmute(0_u32) }; let _: char = unsafe { std::mem::transmute(0_i32) }; + + // These shouldn't warn + const _: char = unsafe { std::mem::transmute(0_u32) }; + const _: char = unsafe { std::mem::transmute(0_i32) }; } #[warn(clippy::transmute_int_to_bool)] @@ -130,9 +134,12 @@ mod num_to_bytes { } } -fn bytes_to_str(b: &[u8], mb: &mut [u8]) { - let _: &str = unsafe { std::mem::transmute(b) }; +fn bytes_to_str(mb: &mut [u8]) { + const B: &[u8] = b""; + + let _: &str = unsafe { std::mem::transmute(B) }; let _: &mut str = unsafe { std::mem::transmute(mb) }; + const _: &str = unsafe { std::mem::transmute(B) }; } fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr index 86537153e3228..72a386e8fa618 100644 --- a/src/tools/clippy/tests/ui/transmute.stderr +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -107,7 +107,7 @@ LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:80:28 + --> $DIR/transmute.rs:84:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -115,7 +115,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:86:31 + --> $DIR/transmute.rs:90:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` @@ -123,25 +123,25 @@ LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:87:31 + --> $DIR/transmute.rs:91:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `u64` to a `f64` - --> $DIR/transmute.rs:88:31 + --> $DIR/transmute.rs:92:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` error: transmute from a `i64` to a `f64` - --> $DIR/transmute.rs:89:31 + --> $DIR/transmute.rs:93:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:109:30 + --> $DIR/transmute.rs:113:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -149,96 +149,102 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:110:30 + --> $DIR/transmute.rs:114:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:111:31 + --> $DIR/transmute.rs:115:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:112:30 + --> $DIR/transmute.rs:116:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:113:30 + --> $DIR/transmute.rs:117:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:114:31 + --> $DIR/transmute.rs:118:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> $DIR/transmute.rs:115:30 + --> $DIR/transmute.rs:119:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> $DIR/transmute.rs:116:30 + --> $DIR/transmute.rs:120:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:121:30 + --> $DIR/transmute.rs:125:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:122:30 + --> $DIR/transmute.rs:126:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:123:31 + --> $DIR/transmute.rs:127:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:124:30 + --> $DIR/transmute.rs:128:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:125:30 + --> $DIR/transmute.rs:129:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:126:31 + --> $DIR/transmute.rs:130:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:134:28 + --> $DIR/transmute.rs:140:28 | -LL | let _: &str = unsafe { std::mem::transmute(b) }; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` +LL | let _: &str = unsafe { std::mem::transmute(B) }; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` | = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:135:32 + --> $DIR/transmute.rs:141:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 38 previous errors +error: transmute from a `&[u8]` to a `&str` + --> $DIR/transmute.rs:142:30 + | +LL | const _: &str = unsafe { std::mem::transmute(B) }; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` + +error: aborting due to 39 previous errors diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs index 380303d8152aa..afa337c45f417 100644 --- a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs @@ -1,5 +1,9 @@ +// aux-build:proc_macro_unsafe.rs + #![warn(clippy::undocumented_unsafe_blocks)] +extern crate proc_macro_unsafe; + // Valid comments fn nested_local() { @@ -89,11 +93,6 @@ fn block_comment_newlines() { unsafe {} } -#[rustfmt::skip] -fn inline_block_comment() { - /* Safety: */unsafe {} -} - fn block_comment_with_extras() { /* This is a description * SAFETY: @@ -209,8 +208,54 @@ fn local_nest() { let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})]; } +fn in_fn_call(x: *const u32) { + fn f(x: u32) {} + + // Safety: reason + f(unsafe { *x }); +} + +fn multi_in_fn_call(x: *const u32) { + fn f(x: u32, y: u32) {} + + // Safety: reason + f(unsafe { *x }, unsafe { *x }); +} + +fn in_multiline_fn_call(x: *const u32) { + fn f(x: u32, y: u32) {} + + f( + // Safety: reason + unsafe { *x }, + 0, + ); +} + +fn in_macro_call(x: *const u32) { + // Safety: reason + println!("{}", unsafe { *x }); +} + +fn in_multiline_macro_call(x: *const u32) { + println!( + "{}", + // Safety: reason + unsafe { *x }, + ); +} + +fn from_proc_macro() { + proc_macro_unsafe::unsafe_block!(token); +} + // Invalid comments +#[rustfmt::skip] +fn inline_block_comment() { + /* Safety: */ unsafe {} +} + fn no_comment() { unsafe {} } diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr index f69d0da54e0d6..856a07fd31685 100644 --- a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr +++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr @@ -1,114 +1,110 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:215:5 + --> $DIR/undocumented_unsafe_blocks.rs:256:19 + | +LL | /* Safety: */ unsafe {} + | ^^^^^^^^^ + | + = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:260:5 | LL | unsafe {} | ^^^^^^^^^ | - = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` -help: consider adding a safety comment + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:264:14 | -LL ~ // SAFETY: ... -LL + unsafe {} +LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; + | ^^^^^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:219:5 + --> $DIR/undocumented_unsafe_blocks.rs:264:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ | -help: consider adding a safety comment + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:264:48 | -LL ~ // SAFETY: ... -LL + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; +LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; + | ^^^^^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:223:5 + --> $DIR/undocumented_unsafe_blocks.rs:268:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ | -help: consider adding a safety comment + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:268:37 | -LL ~ // SAFETY: ... -LL + let _ = (42, unsafe {}, "test", unsafe {}); +LL | let _ = (42, unsafe {}, "test", unsafe {}); + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:227:5 + --> $DIR/undocumented_unsafe_blocks.rs:272:14 | LL | let _ = *unsafe { &42 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = *unsafe { &42 }; + | ^^^^^^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:232:5 + --> $DIR/undocumented_unsafe_blocks.rs:277:19 | LL | let _ = match unsafe {} { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = match unsafe {} { + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:238:5 + --> $DIR/undocumented_unsafe_blocks.rs:283:14 | LL | let _ = &unsafe {}; - | ^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = &unsafe {}; + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:242:5 + --> $DIR/undocumented_unsafe_blocks.rs:287:14 | LL | let _ = [unsafe {}; 5]; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = [unsafe {}; 5]; + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:246:5 + --> $DIR/undocumented_unsafe_blocks.rs:291:13 | LL | let _ = unsafe {}; - | ^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = unsafe {}; + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:256:8 + --> $DIR/undocumented_unsafe_blocks.rs:301:8 | LL | t!(unsafe {}); | ^^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ t!(// SAFETY: ... -LL ~ unsafe {}); - | + = help: consider adding a safety comment on the preceding line -error: unsafe block in macro expansion missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:262:13 +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:307:13 | LL | unsafe {} | ^^^^^^^^^ @@ -116,56 +112,40 @@ LL | unsafe {} LL | t!(); | ---- in this macro invocation | - = help: consider adding a safety comment in the macro definition + = help: consider adding a safety comment on the preceding line = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:5 + --> $DIR/undocumented_unsafe_blocks.rs:315:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL ~ unsafe {} // SAFETY: - | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:274:5 + --> $DIR/undocumented_unsafe_blocks.rs:319:5 | LL | unsafe { | ^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + unsafe { - | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:284:5 + --> $DIR/undocumented_unsafe_blocks.rs:329:5 | LL | unsafe {}; | ^^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL ~ unsafe {}; - | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:288:20 + --> $DIR/undocumented_unsafe_blocks.rs:333:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ println!("{}", // SAFETY: ... -LL ~ unsafe { String::from_utf8_unchecked(vec![]) }); - | + = help: consider adding a safety comment on the preceding line -error: aborting due to 14 previous errors +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs index b77c19f2ba596..62c3e9636866a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs @@ -30,4 +30,10 @@ fn main() { // do not lint cast to cfg-dependant type 1 as std::os::raw::c_char; + + // do not lint cast to alias type + 1 as I32Alias; + &1 as &I32Alias; } + +type I32Alias = i32; diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed index 3332f49c80c97..36800c5340db2 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed @@ -42,4 +42,9 @@ fn main() { let _ = -1_i32; let _ = -1.0_f32; + + let _ = 1 as I32Alias; + let _ = &1 as &I32Alias; } + +type I32Alias = i32; diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs index ec01e93877927..d4b6bb952ab35 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs @@ -42,4 +42,9 @@ fn main() { let _ = -1 as i32; let _ = -1.0 as f32; + + let _ = 1 as I32Alias; + let _ = &1 as &I32Alias; } + +type I32Alias = i32; diff --git a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs index 690d705573d3f..bafca91917aa4 100644 --- a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs +++ b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs @@ -6,7 +6,7 @@ extern crate serde; use serde::Deserialize; #[derive(Deserialize)] -pub struct A {} +pub struct A; impl A { pub unsafe fn new(_a: i32, _b: i32) -> Self { Self {} @@ -14,13 +14,13 @@ impl A { } #[derive(Deserialize)] -pub struct B {} +pub struct B; impl B { pub unsafe fn unsafe_method(&self) {} } #[derive(Deserialize)] -pub struct C {} +pub struct C; impl C { pub fn unsafe_block(&self) { unsafe {} @@ -28,7 +28,7 @@ impl C { } #[derive(Deserialize)] -pub struct D {} +pub struct D; impl D { pub fn inner_unsafe_fn(&self) { unsafe fn inner() {} @@ -36,7 +36,7 @@ impl D { } // Does not derive `Deserialize`, should be ignored -pub struct E {} +pub struct E; impl E { pub unsafe fn new(_a: i32, _b: i32) -> Self { Self {} @@ -55,12 +55,12 @@ impl E { // Does not have methods using `unsafe`, should be ignored #[derive(Deserialize)] -pub struct F {} +pub struct F; // Check that we honor the `allow` attribute on the ADT #[allow(clippy::unsafe_derive_deserialize)] #[derive(Deserialize)] -pub struct G {} +pub struct G; impl G { pub fn unsafe_block(&self) { unsafe {} diff --git a/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs b/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs index a1f616733bd92..cde4e96d668c2 100644 --- a/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs +++ b/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs @@ -14,8 +14,8 @@ use std::cell::UnsafeCell as Dangerunsafe; use std::cell::UnsafeCell as Bombsawayunsafe; mod mod_with_some_unsafe_things { - pub struct Safe {} - pub struct Unsafe {} + pub struct Safe; + pub struct Unsafe; } use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety; diff --git a/src/tools/clippy/tests/ui/unused_self.rs b/src/tools/clippy/tests/ui/unused_self.rs index 7a4bbdda1ab27..08bf58fec7c3e 100644 --- a/src/tools/clippy/tests/ui/unused_self.rs +++ b/src/tools/clippy/tests/ui/unused_self.rs @@ -5,7 +5,7 @@ mod unused_self { use std::pin::Pin; use std::sync::{Arc, Mutex}; - struct A {} + struct A; impl A { fn unused_self_move(self) {} @@ -27,7 +27,7 @@ mod unused_self { } mod unused_self_allow { - struct A {} + struct A; impl A { // shouldn't trigger @@ -35,7 +35,7 @@ mod unused_self_allow { fn unused_self_move(self) {} } - struct B {} + struct B; // shouldn't trigger #[allow(clippy::unused_self)] @@ -43,7 +43,7 @@ mod unused_self_allow { fn unused_self_move(self) {} } - struct C {} + struct C; #[allow(clippy::unused_self)] impl C { @@ -120,7 +120,7 @@ mod used_self { mod not_applicable { use std::fmt; - struct A {} + struct A; impl fmt::Debug for A { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed index 9d216f56ae60c..3e62ffe74fedd 100644 --- a/src/tools/clippy/tests/ui/use_self.fixed +++ b/src/tools/clippy/tests/ui/use_self.fixed @@ -16,7 +16,7 @@ extern crate proc_macro_derive; fn main() {} mod use_self { - struct Foo {} + struct Foo; impl Foo { fn new() -> Self { @@ -35,7 +35,7 @@ mod use_self { } mod better { - struct Foo {} + struct Foo; impl Foo { fn new() -> Self { @@ -123,7 +123,7 @@ mod macros { }; } - struct Foo {} + struct Foo; impl Foo { use_self_expand!(); // Should not lint in local macros @@ -134,7 +134,7 @@ mod macros { } mod nesting { - struct Foo {} + struct Foo; impl Foo { fn foo() { #[allow(unused_imports)] @@ -209,7 +209,7 @@ mod issue3410 { #[allow(clippy::no_effect, path_statements)] mod rustfix { mod nested { - pub struct A {} + pub struct A; } impl nested::A { @@ -227,7 +227,7 @@ mod rustfix { } mod issue3567 { - struct TestStruct {} + struct TestStruct; impl TestStruct { fn from_something() -> Self { Self {} @@ -248,7 +248,7 @@ mod issue3567 { mod paths_created_by_lowering { use std::ops::Range; - struct S {} + struct S; impl S { const A: usize = 0; @@ -382,7 +382,7 @@ mod issue4305 { } mod lint_at_item_level { - struct Foo {} + struct Foo; #[allow(clippy::use_self)] impl Foo { @@ -400,7 +400,7 @@ mod lint_at_item_level { } mod lint_at_impl_item_level { - struct Foo {} + struct Foo; impl Foo { #[allow(clippy::use_self)] @@ -433,8 +433,8 @@ mod issue4734 { mod nested_paths { use std::convert::Into; mod submod { - pub struct B {} - pub struct C {} + pub struct B; + pub struct C; impl Into for B { fn into(self) -> C { diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs index 5f604fe25e416..da2faddee12a7 100644 --- a/src/tools/clippy/tests/ui/use_self.rs +++ b/src/tools/clippy/tests/ui/use_self.rs @@ -16,7 +16,7 @@ extern crate proc_macro_derive; fn main() {} mod use_self { - struct Foo {} + struct Foo; impl Foo { fn new() -> Foo { @@ -35,7 +35,7 @@ mod use_self { } mod better { - struct Foo {} + struct Foo; impl Foo { fn new() -> Self { @@ -123,7 +123,7 @@ mod macros { }; } - struct Foo {} + struct Foo; impl Foo { use_self_expand!(); // Should not lint in local macros @@ -134,7 +134,7 @@ mod macros { } mod nesting { - struct Foo {} + struct Foo; impl Foo { fn foo() { #[allow(unused_imports)] @@ -209,7 +209,7 @@ mod issue3410 { #[allow(clippy::no_effect, path_statements)] mod rustfix { mod nested { - pub struct A {} + pub struct A; } impl nested::A { @@ -227,7 +227,7 @@ mod rustfix { } mod issue3567 { - struct TestStruct {} + struct TestStruct; impl TestStruct { fn from_something() -> Self { Self {} @@ -248,7 +248,7 @@ mod issue3567 { mod paths_created_by_lowering { use std::ops::Range; - struct S {} + struct S; impl S { const A: usize = 0; @@ -382,7 +382,7 @@ mod issue4305 { } mod lint_at_item_level { - struct Foo {} + struct Foo; #[allow(clippy::use_self)] impl Foo { @@ -400,7 +400,7 @@ mod lint_at_item_level { } mod lint_at_impl_item_level { - struct Foo {} + struct Foo; impl Foo { #[allow(clippy::use_self)] @@ -433,8 +433,8 @@ mod issue4734 { mod nested_paths { use std::convert::Into; mod submod { - pub struct B {} - pub struct C {} + pub struct B; + pub struct C; impl Into for B { fn into(self) -> C { diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed index a5fcde768f183..ce58a80347b55 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.fixed +++ b/src/tools/clippy/tests/ui/useless_attribute.fixed @@ -42,7 +42,7 @@ mod a { mod b { #[allow(dead_code)] #[allow(unreachable_pub)] - pub struct C {} + pub struct C; } #[allow(unreachable_pub)] diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs index 0396d39e3d54e..c82bb9ba07fd7 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.rs +++ b/src/tools/clippy/tests/ui/useless_attribute.rs @@ -42,7 +42,7 @@ mod a { mod b { #[allow(dead_code)] #[allow(unreachable_pub)] - pub struct C {} + pub struct C; } #[allow(unreachable_pub)] diff --git a/src/tools/clippy/tests/versioncheck.rs b/src/tools/clippy/tests/versioncheck.rs index 77102b8cac0c9..38498ebdcf2c1 100644 --- a/src/tools/clippy/tests/versioncheck.rs +++ b/src/tools/clippy/tests/versioncheck.rs @@ -3,34 +3,32 @@ #![allow(clippy::single_match_else)] use rustc_tools_util::VersionInfo; +use std::fs; #[test] fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { + fn read_version(path: &str) -> String { + let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{}`: {:?}", path, e)); + contents + .lines() + .filter_map(|l| l.split_once('=')) + .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim())) + .unwrap_or_else(|| panic!("error finding version in `{}`", path)) + .to_string() + } + // do not run this test inside the upstream rustc repo: // https://github.com/rust-lang/rust-clippy/issues/6683 if option_env!("RUSTC_TEST_SUITE").is_some() { return; } - let clippy_meta = cargo_metadata::MetadataCommand::new() - .no_deps() - .exec() - .expect("could not obtain cargo metadata"); + let clippy_version = read_version("Cargo.toml"); + let clippy_lints_version = read_version("clippy_lints/Cargo.toml"); + let clippy_utils_version = read_version("clippy_utils/Cargo.toml"); - for krate in &["clippy_lints", "clippy_utils"] { - let krate_meta = cargo_metadata::MetadataCommand::new() - .current_dir(std::env::current_dir().unwrap().join(krate)) - .no_deps() - .exec() - .expect("could not obtain cargo metadata"); - assert_eq!(krate_meta.packages[0].version, clippy_meta.packages[0].version); - for package in &clippy_meta.packages[0].dependencies { - if package.name == *krate { - assert!(package.req.matches(&krate_meta.packages[0].version)); - break; - } - } - } + assert_eq!(clippy_version, clippy_lints_version); + assert_eq!(clippy_version, clippy_utils_version); } #[test]