Merge commit '95c0459217d1661edfa794c8bb122452b92fb485' into clippyup

This commit is contained in:
flip1995
2021-01-30 18:06:34 +01:00
parent c18d6f1ffa
commit ac912be984
114 changed files with 6061 additions and 454 deletions

View File

@@ -1,6 +1,7 @@
[alias] [alias]
uitest = "test --test compile-test" uitest = "test --test compile-test"
dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
dev-lintcheck = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --features lintcheck -- lintcheck"
[build] [build]
rustflags = ["-Zunstable-options"] rustflags = ["-Zunstable-options"]

View File

@@ -1878,6 +1878,7 @@ Released 2018-09-13
[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`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_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation [`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap [`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
@@ -1937,6 +1938,8 @@ Released 2018-09-13
[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence [`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 [`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
[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit [`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call [`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used [`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
@@ -1996,6 +1999,7 @@ Released 2018-09-13
[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax [`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax [`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body [`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
@@ -2033,6 +2037,8 @@ Released 2018-09-13
[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
@@ -2161,6 +2167,7 @@ Released 2018-09-13
[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate [`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
@@ -2271,6 +2278,7 @@ Released 2018-09-13
[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
[`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms
[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding [`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding

View File

@@ -310,10 +310,19 @@ currently. Between writing new lints, fixing issues, reviewing pull requests and
responding to issues there may not always be enough time to stay on top of it responding to issues there may not always be enough time to stay on top of it
all. all.
Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug]. We don't Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug], for example
an ICE in a popular crate that many other crates depend on. We don't
want Clippy to crash on your code and we want it to be as reliable as the want Clippy to crash on your code and we want it to be as reliable as the
suggestions from Rust compiler errors. suggestions from Rust compiler errors.
We have prioritization labels and a sync-blocker label, which are described below.
- [P-low][p-low]: Requires attention (fix/response/evaluation) by a team member but isn't urgent.
- [P-medium][p-medium]: Should be addressed by a team member until the next sync.
- [P-high][p-high]: Should be immediately addressed and will require an out-of-cycle sync or a backport.
- [L-sync-blocker][l-sync-blocker]: An issue that "blocks" a sync.
Or rather: before the sync this should be addressed,
e.g. by removing a lint again, so it doesn't hit beta/stable.
## Bors and Homu ## Bors and Homu
We use a bot powered by [Homu][homu] to help automate testing and landing of pull We use a bot powered by [Homu][homu] to help automate testing and landing of pull
@@ -327,6 +336,10 @@ commands [here][homu_instructions].
[triage]: https://forge.rust-lang.org/release/triage-procedure.html [triage]: https://forge.rust-lang.org/release/triage-procedure.html
[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash [l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash
[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug [l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug
[p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low
[p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium
[p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high
[l-sync-blocker]: https://github.com/rust-lang/rust-clippy/labels/L-sync-blocker
[homu]: https://github.com/rust-lang/homu [homu]: https://github.com/rust-lang/homu
[homu_instructions]: https://bors.rust-lang.org/ [homu_instructions]: https://bors.rust-lang.org/
[homu_queue]: https://bors.rust-lang.org/queue/clippy [homu_queue]: https://bors.rust-lang.org/queue/clippy

View File

@@ -4,14 +4,22 @@ version = "0.0.1"
authors = ["Philipp Hansch <dev@phansch.net>"] authors = ["Philipp Hansch <dev@phansch.net>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
bytecount = "0.6" bytecount = "0.6"
clap = "2.33" clap = "2.33"
flate2 = { version = "1.0.19", optional = true }
itertools = "0.9" itertools = "0.9"
opener = "0.4" opener = "0.4"
regex = "1" regex = "1"
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0", optional = true }
shell-escape = "0.1" shell-escape = "0.1"
tar = { version = "0.4.30", optional = true }
toml = { version = "0.5", optional = true }
ureq = { version = "2.0.0-rc3", optional = true }
walkdir = "2" walkdir = "2"
[features] [features]
lintcheck = ["flate2", "serde_json", "tar", "toml", "ureq", "serde"]
deny-warnings = [] deny-warnings = []

View File

@@ -0,0 +1,20 @@
[crates]
# some of these are from cargotest
cargo = ['0.49.0']
iron = ['0.6.1']
ripgrep = ['12.1.1']
xsv = ['0.13.0']
#tokei = ['12.0.4']
rayon = ['1.5.0']
serde = ['1.0.118']
# top 10 crates.io dls
bitflags = ['1.2.1']
libc = ['0.2.81']
log = ['0.4.11']
proc-macro2 = ['1.0.24']
quote = ['1.0.7']
rand = ['0.7.3']
rand_core = ['0.6.0']
regex = ['1.3.2']
syn = ['1.0.54']
unicode-xid = ['0.2.1']

View File

@@ -12,6 +12,7 @@ use walkdir::WalkDir;
pub mod bless; pub mod bless;
pub mod fmt; pub mod fmt;
pub mod lintcheck;
pub mod new_lint; pub mod new_lint;
pub mod ra_setup; pub mod ra_setup;
pub mod serve; pub mod serve;

286
clippy_dev/src/lintcheck.rs Normal file
View File

@@ -0,0 +1,286 @@
// Run clippy on a fixed set of crates and collect the warnings.
// This helps observing the impact clippy changs have on a set of real-world code.
//
// When a new lint is introduced, we can search the results for new warnings and check for false
// positives.
#![cfg(feature = "lintcheck")]
#![allow(clippy::filter_map)]
use crate::clippy_project_root;
use std::collections::HashMap;
use std::process::Command;
use std::{fmt, fs::write, path::PathBuf};
use clap::ArgMatches;
use serde::{Deserialize, Serialize};
use serde_json::Value;
// use this to store the crates when interacting with the crates.toml file
#[derive(Debug, Serialize, Deserialize)]
struct CrateList {
crates: HashMap<String, Vec<String>>,
}
// crate data we stored in the toml, can have multiple versions per crate
// A single TomlCrate is laster mapped to several CrateSources in that case
struct TomlCrate {
name: String,
versions: Vec<String>,
}
// represents an archive we download from crates.io
#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq)]
struct CrateSource {
name: String,
version: String,
}
// represents the extracted sourcecode of a crate
#[derive(Debug)]
struct Crate {
version: String,
name: String,
// path to the extracted sources that clippy can check
path: PathBuf,
}
#[derive(Debug)]
struct ClippyWarning {
crate_name: String,
crate_version: String,
file: String,
line: String,
column: String,
linttype: String,
message: String,
}
impl std::fmt::Display for ClippyWarning {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
r#"{}-{}/{}:{}:{} {} "{}""#,
&self.crate_name, &self.crate_version, &self.file, &self.line, &self.column, &self.linttype, &self.message
)
}
}
impl CrateSource {
fn download_and_extract(&self) -> Crate {
let extract_dir = PathBuf::from("target/lintcheck/crates");
let krate_download_dir = PathBuf::from("target/lintcheck/downloads");
// url to download the crate from crates.io
let url = format!(
"https://crates.io/api/v1/crates/{}/{}/download",
self.name, self.version
);
println!("Downloading and extracting {} {} from {}", self.name, self.version, url);
let _ = std::fs::create_dir("target/lintcheck/");
let _ = std::fs::create_dir(&krate_download_dir);
let _ = std::fs::create_dir(&extract_dir);
let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", &self.name, &self.version));
// don't download/extract if we already have done so
if !krate_file_path.is_file() {
// create a file path to download and write the crate data into
let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
let mut krate_req = ureq::get(&url).call().unwrap().into_reader();
// copy the crate into the file
std::io::copy(&mut krate_req, &mut krate_dest).unwrap();
// unzip the tarball
let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap());
// extract the tar archive
let mut archive = tar::Archive::new(ungz_tar);
archive.unpack(&extract_dir).expect("Failed to extract!");
}
// crate is extracted, return a new Krate object which contains the path to the extracted
// sources that clippy can check
Crate {
version: self.version.clone(),
name: self.name.clone(),
path: extract_dir.join(format!("{}-{}/", self.name, self.version)),
}
}
}
impl Crate {
fn run_clippy_lints(&self, cargo_clippy_path: &PathBuf) -> Vec<ClippyWarning> {
println!("Linting {} {}...", &self.name, &self.version);
let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap();
let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir/");
let all_output = std::process::Command::new(cargo_clippy_path)
.env("CARGO_TARGET_DIR", shared_target_dir)
// lint warnings will look like this:
// src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
.args(&[
"--",
"--message-format=json",
"--",
"--cap-lints=warn",
"-Wclippy::pedantic",
"-Wclippy::cargo",
])
.current_dir(&self.path)
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&all_output.stdout);
let output_lines = stdout.lines();
//dbg!(&output_lines);
let warnings: Vec<ClippyWarning> = output_lines
.into_iter()
// get all clippy warnings
.filter(|line| line.contains("clippy::"))
.map(|json_msg| parse_json_message(json_msg, &self))
.collect();
warnings
}
}
fn build_clippy() {
Command::new("cargo")
.arg("build")
.output()
.expect("Failed to build clippy!");
}
// get a list of CrateSources we want to check from a "lintcheck_crates.toml" file.
fn read_crates() -> Vec<CrateSource> {
let toml_path = PathBuf::from("clippy_dev/lintcheck_crates.toml");
let toml_content: String =
std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
let crate_list: CrateList =
toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e));
// parse the hashmap of the toml file into a list of crates
let tomlcrates: Vec<TomlCrate> = crate_list
.crates
.into_iter()
.map(|(name, versions)| TomlCrate { name, versions })
.collect();
// flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
// multiple Cratesources)
let mut crate_sources = Vec::new();
tomlcrates.into_iter().for_each(|tk| {
tk.versions.iter().for_each(|ver| {
crate_sources.push(CrateSource {
name: tk.name.clone(),
version: ver.to_string(),
});
})
});
crate_sources
}
// extract interesting data from a json lint message
fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e));
ClippyWarning {
crate_name: krate.name.to_string(),
crate_version: krate.version.to_string(),
file: jmsg["message"]["spans"][0]["file_name"]
.to_string()
.trim_matches('"')
.into(),
line: jmsg["message"]["spans"][0]["line_start"]
.to_string()
.trim_matches('"')
.into(),
column: jmsg["message"]["spans"][0]["text"][0]["highlight_start"]
.to_string()
.trim_matches('"')
.into(),
linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(),
message: jmsg["message"]["message"].to_string().trim_matches('"').into(),
}
}
// the main fn
pub fn run(clap_config: &ArgMatches) {
let cargo_clippy_path: PathBuf = PathBuf::from("target/debug/cargo-clippy");
println!("Compiling clippy...");
build_clippy();
println!("Done compiling");
// assert that clippy is found
assert!(
cargo_clippy_path.is_file(),
"target/debug/cargo-clippy binary not found! {}",
cargo_clippy_path.display()
);
let clippy_ver = std::process::Command::new("target/debug/cargo-clippy")
.arg("--version")
.output()
.map(|o| String::from_utf8_lossy(&o.stdout).into_owned())
.expect("could not get clippy version!");
// download and extract the crates, then run clippy on them and collect clippys warnings
// flatten into one big list of warnings
let crates = read_crates();
let clippy_warnings: Vec<ClippyWarning> = if let Some(only_one_crate) = clap_config.value_of("only") {
// if we don't have the specified crated in the .toml, throw an error
if !crates.iter().any(|krate| krate.name == only_one_crate) {
eprintln!(
"ERROR: could not find crate '{}' in clippy_dev/lintcheck_crates.toml",
only_one_crate
);
std::process::exit(1);
}
// only check a single crate that was passed via cmdline
crates
.into_iter()
.map(|krate| krate.download_and_extract())
.filter(|krate| krate.name == only_one_crate)
.map(|krate| krate.run_clippy_lints(&cargo_clippy_path))
.flatten()
.collect()
} else {
// check all crates (default)
crates
.into_iter()
.map(|krate| krate.download_and_extract())
.map(|krate| krate.run_clippy_lints(&cargo_clippy_path))
.flatten()
.collect()
};
// generate some stats:
// count lint type occurrences
let mut counter: HashMap<&String, usize> = HashMap::new();
clippy_warnings
.iter()
.for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1);
// collect into a tupled list for sorting
let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect();
// sort by "000{count} {clippy::lintname}"
// to not have a lint with 200 and 2 warnings take the same spot
stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint));
let stats_formatted: String = stats
.iter()
.map(|(lint, count)| format!("{} {}\n", lint, count))
.collect::<String>();
let mut all_msgs: Vec<String> = clippy_warnings.iter().map(|warning| warning.to_string()).collect();
all_msgs.sort();
all_msgs.push("\n\n\n\nStats\n\n".into());
all_msgs.push(stats_formatted);
// save the text into lintcheck-logs/logs.txt
let mut text = clippy_ver; // clippy version number on top
text.push_str(&format!("\n{}", all_msgs.join("")));
write("lintcheck-logs/logs.txt", text).unwrap();
}

View File

@@ -3,6 +3,9 @@
use clap::{App, Arg, ArgMatches, SubCommand}; use clap::{App, Arg, ArgMatches, SubCommand};
use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints}; use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};
#[cfg(feature = "lintcheck")]
use clippy_dev::lintcheck;
fn main() { fn main() {
let matches = get_clap_config(); let matches = get_clap_config();
@@ -10,6 +13,10 @@ fn main() {
("bless", Some(matches)) => { ("bless", Some(matches)) => {
bless::bless(matches.is_present("ignore-timestamp")); bless::bless(matches.is_present("ignore-timestamp"));
}, },
#[cfg(feature = "lintcheck")]
("lintcheck", Some(matches)) => {
lintcheck::run(&matches);
},
("fmt", Some(matches)) => { ("fmt", Some(matches)) => {
fmt::run(matches.is_present("check"), matches.is_present("verbose")); fmt::run(matches.is_present("check"), matches.is_present("verbose"));
}, },
@@ -46,7 +53,18 @@ fn main() {
} }
fn get_clap_config<'a>() -> ArgMatches<'a> { fn get_clap_config<'a>() -> ArgMatches<'a> {
App::new("Clippy developer tooling") #[cfg(feature = "lintcheck")]
let lintcheck_sbcmd = SubCommand::with_name("lintcheck")
.about("run clippy on a set of crates and check output")
.arg(
Arg::with_name("only")
.takes_value(true)
.value_name("CRATE")
.long("only")
.help("only process a single crate of the list"),
);
let app = App::new("Clippy developer tooling")
.subcommand( .subcommand(
SubCommand::with_name("bless") SubCommand::with_name("bless")
.about("bless the test output changes") .about("bless the test output changes")
@@ -163,6 +181,10 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
.validator_os(serve::validate_port), .validator_os(serve::validate_port),
) )
.arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")), .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
) );
.get_matches()
#[cfg(feature = "lintcheck")]
let app = app.subcommand(lintcheck_sbcmd);
app.get_matches()
} }

View File

@@ -1,5 +1,3 @@
#![allow(clippy::filter_map)]
use std::fs; use std::fs;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;

View File

@@ -8,6 +8,14 @@ use crate::utils::span_lint_and_help;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usage of `as` conversions. /// **What it does:** Checks for usage of `as` conversions.
/// ///
/// Note that this lint is specialized in linting *every single* use of `as`
/// regardless of whether good alternatives exist or not.
/// If you want more precise lints for `as`, please consider using these separate lints:
/// `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,
/// `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.
/// There is a good explanation the reason why this lint should work in this way and how it is useful
/// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).
///
/// **Why is this bad?** `as` conversions will perform many kinds of /// **Why is this bad?** `as` conversions will perform many kinds of
/// conversions, including silently lossy conversions and dangerous coercions. /// conversions, including silently lossy conversions and dangerous coercions.
/// There are cases when it makes sense to use `as`, so the lint is /// There are cases when it makes sense to use `as`, so the lint is

View File

@@ -18,7 +18,7 @@ declare_clippy_lint! {
/// other solution is to ensure the mutex is unlocked before calling await, /// other solution is to ensure the mutex is unlocked before calling await,
/// either by introducing a scope or an explicit call to Drop::drop. /// either by introducing a scope or an explicit call to Drop::drop.
/// ///
/// **Known problems:** None. /// **Known problems:** Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).
/// ///
/// **Example:** /// **Example:**
/// ///
@@ -57,7 +57,7 @@ declare_clippy_lint! {
/// at runtime. Holding onto a `RefCell` ref across an `await` suspension point /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
/// risks panics from a mutable ref shared while other refs are outstanding. /// risks panics from a mutable ref shared while other refs are outstanding.
/// ///
/// **Known problems:** None. /// **Known problems:** Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).
/// ///
/// **Example:** /// **Example:**
/// ///

View File

@@ -0,0 +1,86 @@
use crate::utils::paths::STRING;
use crate::utils::{match_def_path, span_lint_and_help};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind, PathSegment};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{source_map::Spanned, Span};
declare_clippy_lint! {
/// **What it does:**
/// Checks for calls to `ends_with` with possible file extensions
/// and suggests to use a case-insensitive approach instead.
///
/// **Why is this bad?**
/// `ends_with` is case-sensitive and may not detect files with a valid extension.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// fn is_rust_file(filename: &str) -> bool {
/// filename.ends_with(".rs")
/// }
/// ```
/// Use instead:
/// ```rust
/// fn is_rust_file(filename: &str) -> bool {
/// filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true)
/// }
/// ```
pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
pedantic,
"Checks for calls to ends_with with case-sensitive file extensions"
}
declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]);
fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> {
if_chain! {
if let ExprKind::MethodCall(PathSegment { ident, .. }, _, [obj, extension, ..], span) = expr.kind;
if ident.as_str() == "ends_with";
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind;
if (2..=6).contains(&ext_literal.as_str().len());
if ext_literal.as_str().starts_with('.');
if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_digit(10))
|| ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_digit(10));
then {
let mut ty = ctx.typeck_results().expr_ty(obj);
ty = match ty.kind() {
ty::Ref(_, ty, ..) => ty,
_ => ty
};
match ty.kind() {
ty::Str => {
return Some(span);
},
ty::Adt(&ty::AdtDef { did, .. }, _) => {
if match_def_path(ctx, did, &STRING) {
return Some(span);
}
},
_ => { return None; }
}
}
}
None
}
impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
span_lint_and_help(
ctx,
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
span,
"case-sensitive file extension comparison",
None,
"consider using a case-insensitive comparison instead",
);
}
}
}

View File

@@ -57,9 +57,9 @@ impl CognitiveComplexity {
let expr = &body.value; let expr = &body.value;
let mut helper = CCHelper { cc: 1, returns: 0 }; let mut helper = CcHelper { cc: 1, returns: 0 };
helper.visit_expr(expr); helper.visit_expr(expr);
let CCHelper { cc, returns } = helper; let CcHelper { cc, returns } = helper;
let ret_ty = cx.typeck_results().node_type(expr.hir_id); let ret_ty = cx.typeck_results().node_type(expr.hir_id);
let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::result_type) { let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::result_type) {
returns returns
@@ -136,12 +136,12 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity {
} }
} }
struct CCHelper { struct CcHelper {
cc: u64, cc: u64,
returns: u64, returns: u64,
} }
impl<'tcx> Visitor<'tcx> for CCHelper { impl<'tcx> Visitor<'tcx> for CcHelper {
type Map = Map<'tcx>; type Map = Map<'tcx>;
fn visit_expr(&mut self, e: &'tcx Expr<'_>) { fn visit_expr(&mut self, e: &'tcx Expr<'_>) {

View File

@@ -2,7 +2,7 @@ use crate::utils::visitors::LocalUsedVisitor;
use crate::utils::{span_lint_and_then, SpanlessEq}; use crate::utils::{span_lint_and_then, SpanlessEq};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind}; use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{DefIdTree, TyCtxt}; use rustc_middle::ty::{DefIdTree, TyCtxt};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -72,8 +72,7 @@ fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) {
if arms_inner.iter().all(|arm| arm.guard.is_none()); if arms_inner.iter().all(|arm| arm.guard.is_none());
// match expression must be a local binding // match expression must be a local binding
// match <local> { .. } // match <local> { .. }
if let ExprKind::Path(QPath::Resolved(None, path)) = expr_in.kind; if let Some(binding_id) = addr_adjusted_binding(expr_in, cx);
if let Res::Local(binding_id) = path.res;
// one of the branches must be "wild-like" // one of the branches must be "wild-like"
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx)); if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx));
let (wild_inner_arm, non_wild_inner_arm) = let (wild_inner_arm, non_wild_inner_arm) =
@@ -85,7 +84,12 @@ fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) {
// the "wild-like" branches must be equal // the "wild-like" branches must be equal
if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body); if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body);
// the binding must not be used in the if guard // the binding must not be used in the if guard
if !matches!(arm.guard, Some(Guard::If(guard)) if LocalUsedVisitor::new(binding_id).check_expr(guard)); if match arm.guard {
None => true,
Some(Guard::If(expr) | Guard::IfLet(_, expr)) => {
!LocalUsedVisitor::new(binding_id).check_expr(expr)
}
};
// ...or anywhere in the inner match // ...or anywhere in the inner match
if !arms_inner.iter().any(|arm| LocalUsedVisitor::new(binding_id).check_arm(arm)); if !arms_inner.iter().any(|arm| LocalUsedVisitor::new(binding_id).check_arm(arm));
then { then {
@@ -170,3 +174,20 @@ fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool {
} }
false false
} }
/// Retrieves a binding ID with optional `&` and/or `*` operators removed. (e.g. `&**foo`)
/// Returns `None` if a non-reference type is de-referenced.
/// For example, if `Vec` is de-referenced to a slice, `None` is returned.
fn addr_adjusted_binding(mut expr: &Expr<'_>, cx: &LateContext<'_>) -> Option<HirId> {
loop {
match expr.kind {
ExprKind::AddrOf(_, _, e) => expr = e,
ExprKind::Path(QPath::Resolved(None, path)) => match path.res {
Res::Local(binding_id) => break Some(binding_id),
_ => break None,
},
ExprKind::Unary(UnOp::UnDeref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
_ => break None,
}
}
}

View File

@@ -179,3 +179,12 @@ declare_deprecated_lint! {
pub UNKNOWN_CLIPPY_LINTS, pub UNKNOWN_CLIPPY_LINTS,
"this lint has been integrated into the `unknown_lints` rustc lint" "this lint has been integrated into the `unknown_lints` rustc lint"
} }
declare_deprecated_lint! {
/// **What it does:** Nothing. This lint has been deprecated.
///
/// **Deprecation reason:** This lint has been replaced by `manual_find_map`, a
/// more specific lint.
pub FIND_MAP,
"this lint has been replaced by `manual_find_map`, a more specific lint"
}

View File

@@ -5,8 +5,8 @@ use crate::consts::{miri_to_const, Constant};
use crate::utils::span_lint; use crate::utils::span_lint;
use rustc_hir::{Item, ItemKind}; use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, IntTy, UintTy};
use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, IntTy, UintTy};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use std::convert::TryFrom; use std::convert::TryFrom;

View File

@@ -0,0 +1,102 @@
use crate::utils::{indent_of, span_lint_and_then};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`
///
/// **Why is this bad?** Exhaustive enums are typically fine, but a project which does
/// not wish to make a stability commitment around exported enums may wish to
/// disable them by default.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// enum Foo {
/// Bar,
/// Baz
/// }
/// ```
/// Use instead:
/// ```rust
/// #[non_exhaustive]
/// enum Foo {
/// Bar,
/// Baz
/// }
/// ```
pub EXHAUSTIVE_ENUMS,
restriction,
"detects exported enums that have not been marked #[non_exhaustive]"
}
declare_clippy_lint! {
/// **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`
///
/// **Why is this bad?** Exhaustive structs are typically fine, but a project which does
/// not wish to make a stability commitment around exported structs may wish to
/// disable them by default.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// struct Foo {
/// bar: u8,
/// baz: String,
/// }
/// ```
/// Use instead:
/// ```rust
/// #[non_exhaustive]
/// struct Foo {
/// bar: u8,
/// baz: String,
/// }
/// ```
pub EXHAUSTIVE_STRUCTS,
restriction,
"detects exported structs that have not been marked #[non_exhaustive]"
}
declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]);
impl LateLintPass<'_> for ExhaustiveItems {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if_chain! {
if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind;
if cx.access_levels.is_exported(item.hir_id);
if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
then {
let (lint, msg) = if let ItemKind::Enum(..) = item.kind {
(EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive")
} else {
(EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive")
};
let suggestion_span = item.span.shrink_to_lo();
let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
span_lint_and_then(
cx,
lint,
item.span,
msg,
|diag| {
let sugg = format!("#[non_exhaustive]\n{}", indent);
diag.span_suggestion(suggestion_span,
"try adding #[non_exhaustive]",
sugg,
Applicability::MaybeIncorrect);
}
);
}
}
}
}

View File

@@ -92,13 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|db| { |db| {
cx.tcx.infer_ctxt().enter(|infcx| { cx.tcx.infer_ctxt().enter(|infcx| {
for FulfillmentError { obligation, .. } in send_errors { for FulfillmentError { obligation, .. } in send_errors {
infcx.maybe_note_obligation_cause_for_async_await( infcx.maybe_note_obligation_cause_for_async_await(db, &obligation);
db, if let Trait(trait_pred, _) = obligation.predicate.kind().skip_binder() {
&obligation,
);
if let Trait(trait_pred, _) =
obligation.predicate.kind().skip_binder()
{
db.note(&format!( db.note(&format!(
"`{}` doesn't implement `{}`", "`{}` doesn't implement `{}`",
trait_pred.self_ty(), trait_pred.self_ty(),

View File

@@ -46,8 +46,8 @@ declare_lint_pass!(IntPlusOne => [INT_PLUS_ONE]);
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
enum Side { enum Side {
LHS, Lhs,
RHS, Rhs,
} }
impl IntPlusOne { impl IntPlusOne {
@@ -66,11 +66,11 @@ impl IntPlusOne {
match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) {
// `-1 + x` // `-1 + x`
(BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => {
Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS) Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
}, },
// `x - 1` // `x - 1`
(BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS) Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
}, },
_ => None, _ => None,
} }
@@ -82,10 +82,10 @@ impl IntPlusOne {
match (&rhslhs.kind, &rhsrhs.kind) { match (&rhslhs.kind, &rhsrhs.kind) {
// `y + 1` and `1 + y` // `y + 1` and `1 + y`
(&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => {
Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS) Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
}, },
(_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS) Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
}, },
_ => None, _ => None,
} }
@@ -97,10 +97,10 @@ impl IntPlusOne {
match (&lhslhs.kind, &lhsrhs.kind) { match (&lhslhs.kind, &lhsrhs.kind) {
// `1 + x` and `x + 1` // `1 + x` and `x + 1`
(&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => {
Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS) Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
}, },
(_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS) Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
}, },
_ => None, _ => None,
} }
@@ -110,11 +110,11 @@ impl IntPlusOne {
match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) {
// `-1 + y` // `-1 + y`
(BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => {
Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS) Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
}, },
// `y - 1` // `y - 1`
(BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS) Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
}, },
_ => None, _ => None,
} }
@@ -138,8 +138,8 @@ impl IntPlusOne {
if let Some(snippet) = snippet_opt(cx, node.span) { if let Some(snippet) = snippet_opt(cx, node.span) {
if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) { if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
let rec = match side { let rec = match side {
Side::LHS => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)), Side::Lhs => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)),
Side::RHS => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)), Side::Rhs => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)),
}; };
return rec; return rec;
} }

View File

@@ -170,6 +170,7 @@ mod blocks_in_if_conditions;
mod booleans; mod booleans;
mod bytecount; mod bytecount;
mod cargo_common_metadata; mod cargo_common_metadata;
mod case_sensitive_file_extension_comparisons;
mod checked_conversions; mod checked_conversions;
mod cognitive_complexity; mod cognitive_complexity;
mod collapsible_if; mod collapsible_if;
@@ -199,6 +200,7 @@ mod escape;
mod eta_reduction; mod eta_reduction;
mod eval_order_dependence; mod eval_order_dependence;
mod excessive_bools; mod excessive_bools;
mod exhaustive_items;
mod exit; mod exit;
mod explicit_write; mod explicit_write;
mod fallible_impl_from; mod fallible_impl_from;
@@ -300,6 +302,7 @@ mod redundant_closure_call;
mod redundant_else; mod redundant_else;
mod redundant_field_names; mod redundant_field_names;
mod redundant_pub_crate; mod redundant_pub_crate;
mod redundant_slicing;
mod redundant_static_lifetimes; mod redundant_static_lifetimes;
mod ref_option_ref; mod ref_option_ref;
mod reference; mod reference;
@@ -339,6 +342,7 @@ mod unused_self;
mod unused_unit; mod unused_unit;
mod unwrap; mod unwrap;
mod unwrap_in_result; mod unwrap_in_result;
mod upper_case_acronyms;
mod use_self; mod use_self;
mod useless_conversion; mod useless_conversion;
mod vec; mod vec;
@@ -506,6 +510,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
"clippy::unknown_clippy_lints", "clippy::unknown_clippy_lints",
"this lint has been integrated into the `unknown_lints` rustc lint", "this lint has been integrated into the `unknown_lints` rustc lint",
); );
store.register_removed(
"clippy::find_map",
"this lint has been replaced by `manual_find_map`, a more specific lint",
);
// end deprecated lints, do not remove this comment, its used in `update_lints` // end deprecated lints, do not remove this comment, its used in `update_lints`
// begin register lints, do not remove this comment, its used in `update_lints` // begin register lints, do not remove this comment, its used in `update_lints`
@@ -561,6 +569,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&booleans::NONMINIMAL_BOOL, &booleans::NONMINIMAL_BOOL,
&bytecount::NAIVE_BYTECOUNT, &bytecount::NAIVE_BYTECOUNT,
&cargo_common_metadata::CARGO_COMMON_METADATA, &cargo_common_metadata::CARGO_COMMON_METADATA,
&case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
&checked_conversions::CHECKED_CONVERSIONS, &checked_conversions::CHECKED_CONVERSIONS,
&cognitive_complexity::COGNITIVE_COMPLEXITY, &cognitive_complexity::COGNITIVE_COMPLEXITY,
&collapsible_if::COLLAPSIBLE_ELSE_IF, &collapsible_if::COLLAPSIBLE_ELSE_IF,
@@ -610,6 +619,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&eval_order_dependence::EVAL_ORDER_DEPENDENCE, &eval_order_dependence::EVAL_ORDER_DEPENDENCE,
&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
&excessive_bools::STRUCT_EXCESSIVE_BOOLS, &excessive_bools::STRUCT_EXCESSIVE_BOOLS,
&exhaustive_items::EXHAUSTIVE_ENUMS,
&exhaustive_items::EXHAUSTIVE_STRUCTS,
&exit::EXIT, &exit::EXIT,
&explicit_write::EXPLICIT_WRITE, &explicit_write::EXPLICIT_WRITE,
&fallible_impl_from::FALLIBLE_IMPL_FROM, &fallible_impl_from::FALLIBLE_IMPL_FROM,
@@ -731,11 +742,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&methods::FILTER_MAP, &methods::FILTER_MAP,
&methods::FILTER_MAP_NEXT, &methods::FILTER_MAP_NEXT,
&methods::FILTER_NEXT, &methods::FILTER_NEXT,
&methods::FIND_MAP,
&methods::FLAT_MAP_IDENTITY, &methods::FLAT_MAP_IDENTITY,
&methods::FROM_ITER_INSTEAD_OF_COLLECT, &methods::FROM_ITER_INSTEAD_OF_COLLECT,
&methods::GET_UNWRAP, &methods::GET_UNWRAP,
&methods::INEFFICIENT_TO_STRING, &methods::INEFFICIENT_TO_STRING,
&methods::INSPECT_FOR_EACH,
&methods::INTO_ITER_ON_REF, &methods::INTO_ITER_ON_REF,
&methods::ITERATOR_STEP_BY_ZERO, &methods::ITERATOR_STEP_BY_ZERO,
&methods::ITER_CLONED_COLLECT, &methods::ITER_CLONED_COLLECT,
@@ -743,6 +754,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&methods::ITER_NTH, &methods::ITER_NTH,
&methods::ITER_NTH_ZERO, &methods::ITER_NTH_ZERO,
&methods::ITER_SKIP_NEXT, &methods::ITER_SKIP_NEXT,
&methods::MANUAL_FILTER_MAP,
&methods::MANUAL_FIND_MAP,
&methods::MANUAL_SATURATING_ARITHMETIC, &methods::MANUAL_SATURATING_ARITHMETIC,
&methods::MAP_COLLECT_RESULT_UNIT, &methods::MAP_COLLECT_RESULT_UNIT,
&methods::MAP_FLATTEN, &methods::MAP_FLATTEN,
@@ -850,6 +863,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&redundant_else::REDUNDANT_ELSE, &redundant_else::REDUNDANT_ELSE,
&redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_field_names::REDUNDANT_FIELD_NAMES,
&redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_pub_crate::REDUNDANT_PUB_CRATE,
&redundant_slicing::REDUNDANT_SLICING,
&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
&ref_option_ref::REF_OPTION_REF, &ref_option_ref::REF_OPTION_REF,
&reference::DEREF_ADDROF, &reference::DEREF_ADDROF,
@@ -942,6 +956,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&unwrap::PANICKING_UNWRAP, &unwrap::PANICKING_UNWRAP,
&unwrap::UNNECESSARY_UNWRAP, &unwrap::UNNECESSARY_UNWRAP,
&unwrap_in_result::UNWRAP_IN_RESULT, &unwrap_in_result::UNWRAP_IN_RESULT,
&upper_case_acronyms::UPPER_CASE_ACRONYMS,
&use_self::USE_SELF, &use_self::USE_SELF,
&useless_conversion::USELESS_CONVERSION, &useless_conversion::USELESS_CONVERSION,
&vec::USELESS_VEC, &vec::USELESS_VEC,
@@ -981,7 +996,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
} }
store.register_late_pass(|| box utils::author::Author); store.register_late_pass(|| box utils::author::Author);
store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box await_holding_invalid::AwaitHolding);
store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box serde_api::SerdeApi);
let vec_box_size_threshold = conf.vec_box_size_threshold; let vec_box_size_threshold = conf.vec_box_size_threshold;
store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(move || box types::Types::new(vec_box_size_threshold));
store.register_late_pass(|| box booleans::NonminimalBool); store.register_late_pass(|| box booleans::NonminimalBool);
@@ -1092,6 +1107,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence); store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence);
store.register_late_pass(|| box missing_doc::MissingDoc::new()); store.register_late_pass(|| box missing_doc::MissingDoc::new());
store.register_late_pass(|| box missing_inline::MissingInline); store.register_late_pass(|| box missing_inline::MissingInline);
store.register_late_pass(move || box exhaustive_items::ExhaustiveItems);
store.register_late_pass(|| box if_let_some_result::OkIfLet); store.register_late_pass(|| box if_let_some_result::OkIfLet);
store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl); store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
store.register_late_pass(|| box unused_io_amount::UnusedIoAmount); store.register_late_pass(|| box unused_io_amount::UnusedIoAmount);
@@ -1172,6 +1188,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
let enum_variant_name_threshold = conf.enum_variant_name_threshold; let enum_variant_name_threshold = conf.enum_variant_name_threshold;
store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
store.register_early_pass(|| box upper_case_acronyms::UpperCaseAcronyms);
store.register_late_pass(|| box default::Default::default()); store.register_late_pass(|| box default::Default::default());
store.register_late_pass(|| box unused_self::UnusedSelf); store.register_late_pass(|| box unused_self::UnusedSelf);
store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
@@ -1229,6 +1246,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues);
store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default());
store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); store.register_late_pass(move || box types::PtrAsPtr::new(msrv));
store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons);
store.register_late_pass(|| box redundant_slicing::RedundantSlicing);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1239,6 +1258,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&create_dir::CREATE_DIR), LintId::of(&create_dir::CREATE_DIR),
LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&dbg_macro::DBG_MACRO),
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS),
LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS),
LintId::of(&exit::EXIT), LintId::of(&exit::EXIT),
LintId::of(&float_literal::LOSSY_FLOAT_LITERAL), LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
LintId::of(&implicit_return::IMPLICIT_RETURN), LintId::of(&implicit_return::IMPLICIT_RETURN),
@@ -1286,6 +1307,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK),
LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&bit_mask::VERBOSE_BIT_MASK),
LintId::of(&case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
LintId::of(&copy_iterator::COPY_ITERATOR), LintId::of(&copy_iterator::COPY_ITERATOR),
@@ -1323,7 +1345,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&matches::SINGLE_MATCH_ELSE), LintId::of(&matches::SINGLE_MATCH_ELSE),
LintId::of(&methods::FILTER_MAP), LintId::of(&methods::FILTER_MAP),
LintId::of(&methods::FILTER_MAP_NEXT), LintId::of(&methods::FILTER_MAP_NEXT),
LintId::of(&methods::FIND_MAP),
LintId::of(&methods::INEFFICIENT_TO_STRING), LintId::of(&methods::INEFFICIENT_TO_STRING),
LintId::of(&methods::MAP_FLATTEN), LintId::of(&methods::MAP_FLATTEN),
LintId::of(&methods::MAP_UNWRAP_OR), LintId::of(&methods::MAP_UNWRAP_OR),
@@ -1509,6 +1530,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FILTER_NEXT),
LintId::of(&methods::FLAT_MAP_IDENTITY), LintId::of(&methods::FLAT_MAP_IDENTITY),
LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(&methods::INSPECT_FOR_EACH),
LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::INTO_ITER_ON_REF),
LintId::of(&methods::ITERATOR_STEP_BY_ZERO), LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
LintId::of(&methods::ITER_CLONED_COLLECT), LintId::of(&methods::ITER_CLONED_COLLECT),
@@ -1516,6 +1538,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&methods::ITER_NTH), LintId::of(&methods::ITER_NTH),
LintId::of(&methods::ITER_NTH_ZERO), LintId::of(&methods::ITER_NTH_ZERO),
LintId::of(&methods::ITER_SKIP_NEXT), LintId::of(&methods::ITER_SKIP_NEXT),
LintId::of(&methods::MANUAL_FILTER_MAP),
LintId::of(&methods::MANUAL_FIND_MAP),
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(&methods::MAP_COLLECT_RESULT_UNIT), LintId::of(&methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(&methods::NEW_RET_NO_SELF), LintId::of(&methods::NEW_RET_NO_SELF),
@@ -1589,6 +1613,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&redundant_clone::REDUNDANT_CLONE), LintId::of(&redundant_clone::REDUNDANT_CLONE),
LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL),
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
LintId::of(&redundant_slicing::REDUNDANT_SLICING),
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::DEREF_ADDROF),
LintId::of(&reference::REF_IN_DEREF), LintId::of(&reference::REF_IN_DEREF),
@@ -1651,6 +1676,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unused_unit::UNUSED_UNIT),
LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&unwrap::PANICKING_UNWRAP),
LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP),
LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS),
LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&useless_conversion::USELESS_CONVERSION),
LintId::of(&vec::USELESS_VEC), LintId::of(&vec::USELESS_VEC),
LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH),
@@ -1767,6 +1793,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unused_unit::UNUSED_UNIT),
LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS),
LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINTLN_EMPTY_STRING),
LintId::of(&write::PRINT_LITERAL), LintId::of(&write::PRINT_LITERAL),
LintId::of(&write::PRINT_WITH_NEWLINE), LintId::of(&write::PRINT_WITH_NEWLINE),
@@ -1808,6 +1835,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&methods::CLONE_ON_COPY), LintId::of(&methods::CLONE_ON_COPY),
LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FILTER_NEXT),
LintId::of(&methods::FLAT_MAP_IDENTITY), LintId::of(&methods::FLAT_MAP_IDENTITY),
LintId::of(&methods::INSPECT_FOR_EACH),
LintId::of(&methods::MANUAL_FILTER_MAP),
LintId::of(&methods::MANUAL_FIND_MAP),
LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::OPTION_AS_REF_DEREF),
LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SEARCH_IS_SOME),
LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::SKIP_WHILE_NEXT),
@@ -1832,6 +1862,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL),
LintId::of(&redundant_slicing::REDUNDANT_SLICING),
LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::DEREF_ADDROF),
LintId::of(&reference::REF_IN_DEREF), LintId::of(&reference::REF_IN_DEREF),
LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&repeat_once::REPEAT_ONCE),

View File

@@ -1069,7 +1069,6 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>(
) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> + 'c { ) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> + 'c {
// As the `filter` and `map` below do different things, I think putting together // As the `filter` and `map` below do different things, I think putting together
// just increases complexity. (cc #3188 and #4193) // just increases complexity. (cc #3188 and #4193)
#[allow(clippy::filter_map)]
stmts stmts
.iter() .iter()
.filter_map(move |stmt| match stmt.kind { .filter_map(move |stmt| match stmt.kind {

View File

@@ -43,8 +43,7 @@ impl LateLintPass<'_> for MainRecursion {
if_chain! { if_chain! {
if let ExprKind::Call(func, _) = &expr.kind; if let ExprKind::Call(func, _) = &expr.kind;
if let ExprKind::Path(path) = &func.kind; if let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind;
if let QPath::Resolved(_, path) = &path;
if let Some(def_id) = path.res.opt_def_id(); if let Some(def_id) = path.res.opt_def_id();
if is_entrypoint_fn(cx, def_id); if is_entrypoint_fn(cx, def_id);
then { then {

View File

@@ -23,9 +23,6 @@ declare_clippy_lint! {
/// ```rust /// ```rust
/// let foo: Option<i32> = None; /// let foo: Option<i32> = None;
/// foo.map_or(Err("error"), |v| Ok(v)); /// foo.map_or(Err("error"), |v| Ok(v));
///
/// let foo: Option<i32> = None;
/// foo.map_or(Err("error"), |v| Ok(v));
/// ``` /// ```
/// ///
/// Use instead: /// Use instead:

View File

@@ -131,7 +131,7 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) ->
Some(expr.span) Some(expr.span)
}, },
hir::ExprKind::Block(ref block, _) => { hir::ExprKind::Block(ref block, _) => {
match (&block.stmts[..], block.expr.as_ref()) { match (block.stmts, block.expr.as_ref()) {
(&[], Some(inner_expr)) => { (&[], Some(inner_expr)) => {
// If block only contains an expression, // If block only contains an expression,
// reduce `{ X }` to `X` // reduce `{ X }` to `X`

View File

@@ -2,10 +2,10 @@ use crate::consts::{constant, miri_to_const, Constant};
use crate::utils::sugg::Sugg; use crate::utils::sugg::Sugg;
use crate::utils::usage::is_unused; use crate::utils::usage::is_unused;
use crate::utils::{ use crate::utils::{
expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, expr_block, get_arg_name, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of,
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks, is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg,
snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt,
span_lint_and_sugg, span_lint_and_then, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
}; };
use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
use if_chain::if_chain; use if_chain::if_chain;
@@ -728,20 +728,60 @@ fn report_single_match_single_pattern(
let els_str = els.map_or(String::new(), |els| { let els_str = els.map_or(String::new(), |els| {
format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span))) format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
}); });
span_lint_and_sugg(
cx, let (msg, sugg) = if_chain! {
lint, let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
expr.span, if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
"you seem to be trying to use match for destructuring a single pattern. Consider using `if \ let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
let`", if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait();
"try this", if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]);
format!( then {
// scrutinee derives PartialEq and the pattern is a constant.
let pat_ref_count = match pat.kind {
// string literals are already a reference.
PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
_ => pat_ref_count,
};
// References are only implicitly added to the pattern, so no overflow here.
// e.g. will work: match &Some(_) { Some(_) => () }
// will not: match Some(_) { &Some(_) => () }
let ref_count_diff = ty_ref_count - pat_ref_count;
// Try to remove address of expressions first.
let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff);
let ref_count_diff = ref_count_diff - removed;
let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`";
let sugg = format!(
"if {} == {}{} {}{}",
snippet(cx, ex.span, ".."),
// PartialEq for different reference counts may not exist.
"&".repeat(ref_count_diff),
snippet(cx, arms[0].pat.span, ".."),
expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
els_str,
);
(msg, sugg)
} else {
let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
let sugg = format!(
"if let {} = {} {}{}", "if let {} = {} {}{}",
snippet(cx, arms[0].pat.span, ".."), snippet(cx, arms[0].pat.span, ".."),
snippet(cx, ex.span, ".."), snippet(cx, ex.span, ".."),
expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
els_str, els_str,
), );
(msg, sugg)
}
};
span_lint_and_sugg(
cx,
lint,
expr.span,
msg,
"try this",
sugg,
Applicability::HasPlaceholders, Applicability::HasPlaceholders,
); );
} }
@@ -1185,6 +1225,14 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
} else { } else {
pat pat
}; };
// strip potential borrows (#6503), but only if the type is a reference
let mut ex_new = ex;
if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
if let ty::Ref(..) = cx.typeck_results().expr_ty(&ex_inner).kind() {
ex_new = ex_inner;
}
};
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
MATCH_LIKE_MATCHES_MACRO, MATCH_LIKE_MATCHES_MACRO,
@@ -1194,7 +1242,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
format!( format!(
"{}matches!({}, {})", "{}matches!({}, {})",
if b0 { "" } else { "!" }, if b0 { "" } else { "!" },
snippet_with_applicability(cx, ex.span, "..", &mut applicability), snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
pat_and_guard, pat_and_guard,
), ),
applicability, applicability,

View File

@@ -0,0 +1,23 @@
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::source_map::Span;
use crate::utils::{match_trait_method, paths, span_lint_and_help};
use super::INSPECT_FOR_EACH;
/// lint use of `inspect().for_each()` for `Iterators`
pub(super) fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) {
if match_trait_method(cx, expr, &paths::ITERATOR) {
let msg = "called `inspect(..).for_each(..)` on an `Iterator`";
let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`";
span_lint_and_help(
cx,
INSPECT_FOR_EACH,
inspect_span.with_hi(expr.span.hi()),
msg,
None,
hint,
);
}
}

View File

@@ -1,5 +1,6 @@
mod bind_instead_of_map; mod bind_instead_of_map;
mod inefficient_to_string; mod inefficient_to_string;
mod inspect_for_each;
mod manual_saturating_arithmetic; mod manual_saturating_arithmetic;
mod option_map_unwrap_or; mod option_map_unwrap_or;
mod unnecessary_filter_map; mod unnecessary_filter_map;
@@ -14,7 +15,8 @@ use if_chain::if_chain;
use rustc_ast::ast; use rustc_ast::ast;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::{TraitItem, TraitItemKind}; use rustc_hir::def::Res;
use rustc_hir::{Expr, ExprKind, PatKind, QPath, TraitItem, TraitItemKind, UnOp};
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_middle::ty::{self, TraitRef, Ty, TyS};
@@ -449,6 +451,58 @@ declare_clippy_lint! {
"using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call" "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call"
} }
declare_clippy_lint! {
/// **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply
/// as `filter_map(_)`.
///
/// **Why is this bad?** Redundant code in the `filter` and `map` operations is poor style and
/// less performant.
///
/// **Known problems:** None.
///
/// **Example:**
/// Bad:
/// ```rust
/// (0_i32..10)
/// .filter(|n| n.checked_add(1).is_some())
/// .map(|n| n.checked_add(1).unwrap());
/// ```
///
/// Good:
/// ```rust
/// (0_i32..10).filter_map(|n| n.checked_add(1));
/// ```
pub MANUAL_FILTER_MAP,
complexity,
"using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
}
declare_clippy_lint! {
/// **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply
/// as `find_map(_)`.
///
/// **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and
/// less performant.
///
/// **Known problems:** None.
///
/// **Example:**
/// Bad:
/// ```rust
/// (0_i32..10)
/// .find(|n| n.checked_add(1).is_some())
/// .map(|n| n.checked_add(1).unwrap());
/// ```
///
/// Good:
/// ```rust
/// (0_i32..10).find_map(|n| n.checked_add(1));
/// ```
pub MANUAL_FIND_MAP,
complexity,
"using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
}
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usage of `_.filter_map(_).next()`. /// **What it does:** Checks for usage of `_.filter_map(_).next()`.
/// ///
@@ -493,28 +547,6 @@ declare_clippy_lint! {
"call to `flat_map` where `flatten` is sufficient" "call to `flat_map` where `flatten` is sufficient"
} }
declare_clippy_lint! {
/// **What it does:** Checks for usage of `_.find(_).map(_)`.
///
/// **Why is this bad?** Readability, this can be written more concisely as
/// `_.find_map(_)`.
///
/// **Known problems:** Often requires a condition + Option/Iterator creation
/// inside the closure.
///
/// **Example:**
/// ```rust
/// (0..3).find(|x| *x == 2).map(|x| x * 2);
/// ```
/// Can be written as
/// ```rust
/// (0..3).find_map(|x| if x == 2 { Some(x * 2) } else { None });
/// ```
pub FIND_MAP,
pedantic,
"using a combination of `find` and `map` can usually be written as a single method call"
}
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for an iterator or string search (such as `find()`, /// **What it does:** Checks for an iterator or string search (such as `find()`,
/// `position()`, or `rposition()`) followed by a call to `is_some()`. /// `position()`, or `rposition()`) followed by a call to `is_some()`.
@@ -1405,6 +1437,36 @@ declare_clippy_lint! {
"use `.collect()` instead of `::from_iter()`" "use `.collect()` instead of `::from_iter()`"
} }
declare_clippy_lint! {
/// **What it does:** Checks for usage of `inspect().for_each()`.
///
/// **Why is this bad?** It is the same as performing the computation
/// inside `inspect` at the beginning of the closure in `for_each`.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// [1,2,3,4,5].iter()
/// .inspect(|&x| println!("inspect the number: {}", x))
/// .for_each(|&x| {
/// assert!(x >= 0);
/// });
/// ```
/// Can be written as
/// ```rust
/// [1,2,3,4,5].iter()
/// .for_each(|&x| {
/// println!("inspect the number: {}", x);
/// assert!(x >= 0);
/// });
/// ```
pub INSPECT_FOR_EACH,
complexity,
"using `.inspect().for_each()`, which can be replaced with `.for_each()`"
}
pub struct Methods { pub struct Methods {
msrv: Option<RustcVersion>, msrv: Option<RustcVersion>,
} }
@@ -1442,9 +1504,10 @@ impl_lint_pass!(Methods => [
FILTER_NEXT, FILTER_NEXT,
SKIP_WHILE_NEXT, SKIP_WHILE_NEXT,
FILTER_MAP, FILTER_MAP,
MANUAL_FILTER_MAP,
MANUAL_FIND_MAP,
FILTER_MAP_NEXT, FILTER_MAP_NEXT,
FLAT_MAP_IDENTITY, FLAT_MAP_IDENTITY,
FIND_MAP,
MAP_FLATTEN, MAP_FLATTEN,
ITERATOR_STEP_BY_ZERO, ITERATOR_STEP_BY_ZERO,
ITER_NEXT_SLICE, ITER_NEXT_SLICE,
@@ -1467,6 +1530,7 @@ impl_lint_pass!(Methods => [
UNNECESSARY_LAZY_EVALUATIONS, UNNECESSARY_LAZY_EVALUATIONS,
MAP_COLLECT_RESULT_UNIT, MAP_COLLECT_RESULT_UNIT,
FROM_ITER_INSTEAD_OF_COLLECT, FROM_ITER_INSTEAD_OF_COLLECT,
INSPECT_FOR_EACH,
]); ]);
impl<'tcx> LateLintPass<'tcx> for Methods { impl<'tcx> LateLintPass<'tcx> for Methods {
@@ -1508,10 +1572,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]),
["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]), ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]),
["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), ["map", "filter"] => lint_filter_map(cx, expr, false),
["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()), ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()),
["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]), ["map", "find"] => lint_filter_map(cx, expr, true),
["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]), ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]),
@@ -1553,6 +1617,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"), ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"),
["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"),
["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]), ["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]),
["for_each", "inspect"] => inspect_for_each::lint(cx, expr, method_spans[1]),
_ => {}, _ => {},
} }
@@ -2955,18 +3020,79 @@ fn lint_skip_while_next<'tcx>(
} }
} }
/// lint use of `filter().map()` for `Iterators` /// lint use of `filter().map()` or `find().map()` for `Iterators`
fn lint_filter_map<'tcx>( fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) {
cx: &LateContext<'tcx>, if_chain! {
expr: &'tcx hir::Expr<'_>, if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind;
_filter_args: &'tcx [hir::Expr<'_>], if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind;
_map_args: &'tcx [hir::Expr<'_>], if match_trait_method(cx, map_recv, &paths::ITERATOR);
) {
// lint if caller of `.filter().map()` is an Iterator // filter(|x| ...is_some())...
if match_trait_method(cx, expr, &paths::ITERATOR) { if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind;
let msg = "called `filter(..).map(..)` on an `Iterator`"; let filter_body = cx.tcx.hir().body(filter_body_id);
let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead"; if let [filter_param] = filter_body.params;
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); // optional ref pattern: `filter(|&x| ..)`
let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
(ref_pat, true)
} else {
(filter_param.pat, false)
};
// closure ends with is_some() or is_ok()
if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind;
if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def();
if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) {
Some(false)
} else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) {
Some(true)
} else {
None
};
if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" };
// ...map(|x| ...unwrap())
if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind;
let map_body = cx.tcx.hir().body(map_body_id);
if let [map_param] = map_body.params;
if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind;
// closure ends with expect() or unwrap()
if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind;
if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
// in `filter(|x| ..)`, replace `*x` with `x`
let a_path = if_chain! {
if !is_filter_param_ref;
if let ExprKind::Unary(UnOp::UnDeref, expr_path) = a.kind;
then { expr_path } else { a }
};
// let the filter closure arg and the map closure arg be equal
if_chain! {
if let ExprKind::Path(QPath::Resolved(None, a_path)) = a_path.kind;
if let ExprKind::Path(QPath::Resolved(None, b_path)) = b.kind;
if a_path.res == Res::Local(filter_param_id);
if b_path.res == Res::Local(map_param_id);
if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b));
then {
return true;
}
}
false
};
if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg);
then {
let span = filter_span.to(map_span);
let (filter_name, lint) = if is_find {
("find", MANUAL_FIND_MAP)
} else {
("filter", MANUAL_FILTER_MAP)
};
let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name);
let to_opt = if is_result { ".ok()" } else { "" };
let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident,
snippet(cx, map_arg.span, ".."), to_opt);
span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
}
} }
} }
@@ -3004,21 +3130,6 @@ fn lint_filter_map_next<'tcx>(
} }
} }
/// lint use of `find().map()` for `Iterators`
fn lint_find_map<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
_find_args: &'tcx [hir::Expr<'_>],
map_args: &'tcx [hir::Expr<'_>],
) {
// lint if caller of `.filter().map()` is an Iterator
if match_trait_method(cx, &map_args[0], &paths::ITERATOR) {
let msg = "called `find(..).map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by calling `.find_map(..)` instead";
span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint);
}
}
/// lint use of `filter_map().map()` for `Iterators` /// lint use of `filter_map().map()` for `Iterators`
fn lint_filter_map_map<'tcx>( fn lint_filter_map_map<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
@@ -3026,7 +3137,7 @@ fn lint_filter_map_map<'tcx>(
_filter_args: &'tcx [hir::Expr<'_>], _filter_args: &'tcx [hir::Expr<'_>],
_map_args: &'tcx [hir::Expr<'_>], _map_args: &'tcx [hir::Expr<'_>],
) { ) {
// lint if caller of `.filter().map()` is an Iterator // lint if caller of `.filter_map().map()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) { if match_trait_method(cx, expr, &paths::ITERATOR) {
let msg = "called `filter_map(..).map(..)` on an `Iterator`"; let msg = "called `filter_map(..).map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead"; let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead";

View File

@@ -117,9 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
.filter_map(|obligation| { .filter_map(|obligation| {
// Note that we do not want to deal with qualified predicates here. // Note that we do not want to deal with qualified predicates here.
match obligation.predicate.kind().no_bound_vars() { match obligation.predicate.kind().no_bound_vars() {
Some(ty::PredicateKind::Trait(pred, _)) if pred.def_id() != sized_trait => { Some(ty::PredicateKind::Trait(pred, _)) if pred.def_id() != sized_trait => Some(pred),
Some(pred)
},
_ => None, _ => None,
} }
}) })

View File

@@ -199,6 +199,10 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
); );
return; return;
} }
if interned_name.starts_with('_') {
// these bindings are typically unused or represent an ignored portion of a destructuring pattern
return;
}
let count = interned_name.chars().count(); let count = interned_name.chars().count();
if count < 3 { if count < 3 {
if count == 1 { if count == 1 {

View File

@@ -263,8 +263,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
} else if match_type(cx, ty, &paths::COW) { } else if match_type(cx, ty, &paths::COW) {
if_chain! { if_chain! {
if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind; if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind;
if let TyKind::Path(ref path) = ty.kind; if let TyKind::Path(QPath::Resolved(None, ref pp)) = ty.kind;
if let QPath::Resolved(None, ref pp) = *path;
if let [ref bx] = *pp.segments; if let [ref bx] = *pp.segments;
if let Some(ref params) = bx.args; if let Some(ref params) = bx.args;
if !params.parenthesized; if !params.parenthesized;

View File

@@ -0,0 +1,67 @@
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::{lint::in_external_macro, ty::TyS};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use crate::utils::{is_type_lang_item, snippet_with_applicability, span_lint_and_sugg};
declare_clippy_lint! {
/// **What it does:** Checks for redundant slicing expressions which use the full range, and
/// do not change the type.
///
/// **Why is this bad?** It unnecessarily adds complexity to the expression.
///
/// **Known problems:** If the type being sliced has an implementation of `Index<RangeFull>`
/// that actually changes anything then it can't be removed. However, this would be surprising
/// to people reading the code and should have a note with it.
///
/// **Example:**
///
/// ```ignore
/// fn get_slice(x: &[u32]) -> &[u32] {
/// &x[..]
/// }
/// ```
/// Use instead:
/// ```ignore
/// fn get_slice(x: &[u32]) -> &[u32] {
/// x
/// }
/// ```
pub REDUNDANT_SLICING,
complexity,
"redundant slicing of the whole range of a type"
}
declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]);
impl LateLintPass<'_> for RedundantSlicing {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if in_external_macro(cx.sess(), expr.span) {
return;
}
if_chain! {
if let ExprKind::AddrOf(_, _, addressee) = expr.kind;
if let ExprKind::Index(indexed, range) = addressee.kind;
if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull);
if TyS::same_type(cx.typeck_results().expr_ty(expr), cx.typeck_results().expr_ty(indexed));
then {
let mut app = Applicability::MachineApplicable;
let hint = snippet_with_applicability(cx, indexed.span, "..", &mut app).into_owned();
span_lint_and_sugg(
cx,
REDUNDANT_SLICING,
expr.span,
"redundant slicing of the whole range",
"use the original slice instead",
hint,
app,
);
}
}
}
}

View File

@@ -1,3 +1,4 @@
use crate::utils::sugg::Sugg;
use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg}; use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
@@ -110,6 +111,12 @@ declare_clippy_lint! {
/// let point = Point(30, 20); /// let point = Point(30, 20);
/// let x = (&point).0; /// let x = (&point).0;
/// ``` /// ```
/// Use instead:
/// ```rust
/// # struct Point(u32, u32);
/// # let point = Point(30, 20);
/// let x = point.0;
/// ```
pub REF_IN_DEREF, pub REF_IN_DEREF,
complexity, complexity,
"Use of reference in auto dereference expression." "Use of reference in auto dereference expression."
@@ -124,14 +131,19 @@ impl EarlyLintPass for RefInDeref {
if let ExprKind::Paren(ref parened) = object.kind; if let ExprKind::Paren(ref parened) = object.kind;
if let ExprKind::AddrOf(_, _, ref inner) = parened.kind; if let ExprKind::AddrOf(_, _, ref inner) = parened.kind;
then { then {
let mut applicability = Applicability::MachineApplicable; let applicability = if inner.span.from_expansion() {
Applicability::MaybeIncorrect
} else {
Applicability::MachineApplicable
};
let sugg = Sugg::ast(cx, inner, "_").maybe_par();
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
REF_IN_DEREF, REF_IN_DEREF,
object.span, object.span,
"creating a reference that is immediately dereferenced", "creating a reference that is immediately dereferenced",
"try this", "try this",
snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(), sugg.to_string(),
applicability, applicability,
); );
} }

View File

@@ -131,7 +131,16 @@ impl<'tcx> LateLintPass<'tcx> for Return {
_: HirId, _: HirId,
) { ) {
match kind { match kind {
FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty), FnKind::Closure(_) => {
// when returning without value in closure, replace this `return`
// with an empty block to prevent invalid suggestion (see #6501)
let replacement = if let ExprKind::Ret(None) = &body.value.kind {
RetReplacement::Block
} else {
RetReplacement::Empty
};
check_final_expr(cx, &body.value, Some(body.value.span), replacement)
},
FnKind::ItemFn(..) | FnKind::Method(..) => { FnKind::ItemFn(..) | FnKind::Method(..) => {
if let ExprKind::Block(ref block, _) = body.value.kind { if let ExprKind::Block(ref block, _) = body.value.kind {
check_block_return(cx, block); check_block_return(cx, block);

View File

@@ -18,9 +18,9 @@ declare_clippy_lint! {
"various things that will negatively affect your serde experience" "various things that will negatively affect your serde experience"
} }
declare_lint_pass!(SerdeAPI => [SERDE_API_MISUSE]); declare_lint_pass!(SerdeApi => [SERDE_API_MISUSE]);
impl<'tcx> LateLintPass<'tcx> for SerdeAPI { impl<'tcx> LateLintPass<'tcx> for SerdeApi {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if let ItemKind::Impl(Impl { if let ItemKind::Impl(Impl {
of_trait: Some(ref trait_ref), of_trait: Some(ref trait_ref),

View File

@@ -35,10 +35,11 @@ declare_clippy_lint! {
declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]);
fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Ty<'tcx>> { fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> {
match expr.kind { match expr.kind {
ExprKind::Call(count_func, _func_args) => { ExprKind::Call(count_func, _func_args) => {
if_chain! { if_chain! {
if !inverted;
if let ExprKind::Path(ref count_func_qpath) = count_func.kind; if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::MEM_SIZE_OF) if match_def_path(cx, def_id, &paths::MEM_SIZE_OF)
@@ -50,10 +51,13 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Ty<'tc
} }
} }
}, },
ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node || BinOpKind::Div == op.node => { ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node => {
get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) get_size_of_ty(cx, left, inverted).or_else(|| get_size_of_ty(cx, right, inverted))
}, },
ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr), ExprKind::Binary(op, left, right) if BinOpKind::Div == op.node => {
get_size_of_ty(cx, left, inverted).or_else(|| get_size_of_ty(cx, right, !inverted))
},
ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr, inverted),
_ => None, _ => None,
} }
} }
@@ -128,7 +132,7 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount {
// Find a size_of call in the count parameter expression and // Find a size_of call in the count parameter expression and
// check that it's the same type // check that it's the same type
if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr); if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr, false);
if TyS::same_type(pointee_ty, ty_used_for_size_of); if TyS::same_type(pointee_ty, ty_used_for_size_of);
then { then {
span_lint_and_help( span_lint_and_help(

View File

@@ -1,4 +1,4 @@
use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg}; use crate::utils::{is_slice_of_primitives, span_lint_and_then, sugg::Sugg};
use if_chain::if_chain; use if_chain::if_chain;
@@ -107,26 +107,33 @@ fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option
impl LateLintPass<'_> for StableSortPrimitive { impl LateLintPass<'_> for StableSortPrimitive {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if let Some(detection) = detect_stable_sort_primitive(cx, expr) { if let Some(detection) = detect_stable_sort_primitive(cx, expr) {
span_lint_and_sugg( span_lint_and_then(
cx, cx,
STABLE_SORT_PRIMITIVE, STABLE_SORT_PRIMITIVE,
expr.span, expr.span,
format!( format!(
"used {} instead of {} to sort primitive type `{}`", "used `{}` on primitive type `{}`",
detection.method.stable_name(), detection.method.stable_name(),
detection.method.unstable_name(),
detection.slice_type, detection.slice_type,
) )
.as_str(), .as_str(),
|diag| {
diag.span_suggestion(
expr.span,
"try", "try",
format!( format!(
"{}.{}({})", "{}.{}({})",
detection.slice_name, detection.slice_name,
detection.method.unstable_name(), detection.method.unstable_name(),
detection.method_args detection.method_args,
), ),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
diag.note(
"an unstable sort would perform faster without any observable difference for this data type",
);
},
);
} }
} }
} }

View File

@@ -0,0 +1,93 @@
use crate::utils::span_lint_and_sugg;
use if_chain::if_chain;
use itertools::Itertools;
use rustc_ast::ast::{Item, ItemKind, Variant};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Ident;
declare_clippy_lint! {
/// **What it does:** Checks for camel case name containing a capitalized acronym.
///
/// **Why is this bad?** In CamelCase, acronyms count as one word.
/// See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case)
/// for more.
///
/// **Known problems:** When two acronyms are contiguous, the lint can't tell where
/// the first acronym ends and the second starts, so it suggests to lowercase all of
/// the letters in the second acronym.
///
/// **Example:**
///
/// ```rust
/// struct HTTPResponse;
/// ```
/// Use instead:
/// ```rust
/// struct HttpResponse;
/// ```
pub UPPER_CASE_ACRONYMS,
style,
"capitalized acronyms are against the naming convention"
}
declare_lint_pass!(UpperCaseAcronyms => [UPPER_CASE_ACRONYMS]);
fn correct_ident(ident: &str) -> String {
let ident = ident.chars().rev().collect::<String>();
let fragments = ident
.split_inclusive(|x: char| !x.is_ascii_lowercase())
.rev()
.map(|x| x.chars().rev().collect::<String>());
let mut ident = fragments.clone().next().unwrap();
for (ref prev, ref curr) in fragments.tuple_windows() {
if [prev, curr]
.iter()
.all(|s| s.len() == 1 && s.chars().next().unwrap().is_ascii_uppercase())
{
ident.push_str(&curr.to_ascii_lowercase());
} else {
ident.push_str(curr);
}
}
ident
}
fn check_ident(cx: &EarlyContext<'_>, ident: &Ident) {
let span = ident.span;
let ident = &ident.as_str();
let corrected = correct_ident(ident);
if ident != &corrected {
span_lint_and_sugg(
cx,
UPPER_CASE_ACRONYMS,
span,
&format!("name `{}` contains a capitalized acronym", ident),
"consider making the acronym lowercase, except the initial letter",
corrected,
Applicability::MaybeIncorrect,
)
}
}
impl EarlyLintPass for UpperCaseAcronyms {
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &Item) {
if_chain! {
if !in_external_macro(cx.sess(), it.span);
if matches!(
it.kind,
ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Trait(..)
);
then {
check_ident(cx, &it.ident);
}
}
}
fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &Variant) {
check_ident(cx, &v.ident);
}
}

View File

@@ -127,6 +127,7 @@ define_Conf! {
"OAuth", "GraphQL", "OAuth", "GraphQL",
"OCaml", "OCaml",
"OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap",
"WebGL",
"TensorFlow", "TensorFlow",
"TrueType", "TrueType",
"iOS", "macOS", "iOS", "macOS",

View File

@@ -186,8 +186,6 @@ pub fn span_lint_hir_and_then(
/// | /// |
/// = note: `-D fold-any` implied by `-D warnings` /// = note: `-D fold-any` implied by `-D warnings`
/// ``` /// ```
#[allow(clippy::unknown_clippy_lints)]
#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))] #[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))]
pub fn span_lint_and_sugg<'a, T: LintContext>( pub fn span_lint_and_sugg<'a, T: LintContext>(
cx: &'a T, cx: &'a T,

View File

@@ -158,8 +158,7 @@ pub fn for_loop<'tcx>(
/// `while cond { body }` becomes `(cond, body)`. /// `while cond { body }` becomes `(cond, body)`.
pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> { pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> {
if_chain! { if_chain! {
if let hir::ExprKind::Loop(block, _, hir::LoopSource::While, _) = &expr.kind; if let hir::ExprKind::Loop(hir::Block { expr: Some(expr), .. }, _, hir::LoopSource::While, _) = &expr.kind;
if let hir::Block { expr: Some(expr), .. } = &**block;
if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind; if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind;
if let hir::ExprKind::DropTemps(cond) = &cond.kind; if let hir::ExprKind::DropTemps(cond) = &cond.kind;
if let [hir::Arm { body, .. }, ..] = &arms[..]; if let [hir::Arm { body, .. }, ..] = &arms[..];

View File

@@ -24,6 +24,7 @@ pub struct SpanlessEq<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
allow_side_effects: bool, allow_side_effects: bool,
expr_fallback: Option<Box<dyn Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
} }
impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
@@ -32,6 +33,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
cx, cx,
maybe_typeck_results: cx.maybe_typeck_results(), maybe_typeck_results: cx.maybe_typeck_results(),
allow_side_effects: true, allow_side_effects: true,
expr_fallback: None,
} }
} }
@@ -43,6 +45,13 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
} }
} }
pub fn expr_fallback(self, expr_fallback: impl Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
Self {
expr_fallback: Some(Box::new(expr_fallback)),
..self
}
}
/// Checks whether two statements are the same. /// Checks whether two statements are the same.
pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
match (&left.kind, &right.kind) { match (&left.kind, &right.kind) {
@@ -81,7 +90,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
} }
} }
match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) { let is_eq = match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) {
(&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => { (&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => {
lb == rb && l_mut == r_mut && self.eq_expr(le, re) lb == rb && l_mut == r_mut && self.eq_expr(le, re)
}, },
@@ -158,7 +167,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
(&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r), (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
(&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re), (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re),
_ => false, _ => false,
} };
is_eq || self.expr_fallback.as_ref().map_or(false, |f| f(left, right))
} }
fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool { fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {

View File

@@ -31,7 +31,6 @@ pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::hash::BuildHasherDefault; use std::hash::BuildHasherDefault;
use std::mem;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::{self, Attribute, LitKind}; use rustc_ast::ast::{self, Attribute, LitKind};
@@ -39,7 +38,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::Node; use rustc_hir::Node;
use rustc_hir::{ use rustc_hir::{
@@ -48,6 +47,7 @@ use rustc_hir::{
}; };
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::exports::Export;
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
@@ -308,65 +308,43 @@ pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
} }
/// Gets the definition associated to a path. /// Gets the definition associated to a path.
pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> { #[allow(clippy::shadow_unrelated)] // false positive #6563
let crates = cx.tcx.crates(); pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<Res> {
let krate = crates fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export<HirId>> {
tcx.item_children(def_id)
.iter() .iter()
.find(|&&krate| cx.tcx.crate_name(krate).as_str() == path[0]); .find(|item| item.ident.name.as_str() == name)
if let Some(krate) = krate { }
let krate = DefId {
krate: *krate,
index: CRATE_DEF_INDEX,
};
let mut current_item = None;
let mut items = cx.tcx.item_children(krate);
let mut path_it = path.iter().skip(1).peekable();
loop { let (krate, first, path) = match *path {
let segment = match path_it.next() { [krate, first, ref path @ ..] => (krate, first, path),
Some(segment) => segment, _ => return None,
None => return None,
}; };
let tcx = cx.tcx;
let crates = tcx.crates();
let krate = crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate)?;
let first = item_child_by_name(tcx, krate.as_def_id(), first)?;
let last = path
.iter()
.copied()
// `get_def_path` seems to generate these empty segments for extern blocks. // `get_def_path` seems to generate these empty segments for extern blocks.
// We can just ignore them. // We can just ignore them.
if segment.is_empty() { .filter(|segment| !segment.is_empty())
continue; // for each segment, find the child item
} .try_fold(first, |item, segment| {
let def_id = item.res.def_id();
let result = SmallVec::<[_; 8]>::new(); if let Some(item) = item_child_by_name(tcx, def_id, segment) {
for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() { Some(item)
if item.ident.name.as_str() == *segment { } else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
if path_it.peek().is_none() { // it is not a child item so check inherent impl items
return Some(item.res); tcx.inherent_impls(def_id)
} .iter()
.find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
current_item = Some(item);
items = cx.tcx.item_children(item.res.def_id());
break;
}
}
// The segment isn't a child_item.
// Try to find it under an inherent impl.
if_chain! {
if path_it.peek().is_none();
if let Some(current_item) = current_item;
let item_def_id = current_item.res.def_id();
if cx.tcx.def_kind(item_def_id) == DefKind::Struct;
then {
// Bad `find_map` suggestion. See #4193.
#[allow(clippy::find_map)]
return cx.tcx.inherent_impls(item_def_id).iter()
.flat_map(|&impl_def_id| cx.tcx.item_children(impl_def_id))
.find(|item| item.ident.name.as_str() == *segment)
.map(|item| item.res);
}
}
}
} else { } else {
None None
} }
})?;
Some(last.res)
} }
/// Convenience function to get the `DefId` of a trait by path. /// Convenience function to get the `DefId` of a trait by path.
@@ -1134,8 +1112,7 @@ pub fn is_self(slf: &Param<'_>) -> bool {
pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool { pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
if_chain! { if_chain! {
if let TyKind::Path(ref qp) = slf.kind; if let TyKind::Path(QPath::Resolved(None, ref path)) = slf.kind;
if let QPath::Resolved(None, ref path) = *qp;
if let Res::SelfTy(..) = path.res; if let Res::SelfTy(..) = path.res;
then { then {
return true return true
@@ -1655,6 +1632,44 @@ where
match_expr_list match_expr_list
} }
/// Peels off all references on the pattern. Returns the underlying pattern and the number of
/// references removed.
pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
if let PatKind::Ref(pat, _) = pat.kind {
peel(pat, count + 1)
} else {
(pat, count)
}
}
peel(pat, 0)
}
/// Peels off up to the given number of references on the expression. Returns the underlying
/// expression and the number of references removed.
pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) {
match expr.kind {
ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target),
_ => (expr, count),
}
}
f(expr, 0, count)
}
/// Peels off all references on the type. Returns the underlying type and the number of references
/// removed.
pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
if let ty::Ref(_, ty, _) = ty.kind() {
peel(ty, count + 1)
} else {
(ty, count)
}
}
peel(ty, 0)
}
#[macro_export] #[macro_export]
macro_rules! unwrap_cargo_metadata { macro_rules! unwrap_cargo_metadata {
($cx: ident, $lint: ident, $deps: expr) => {{ ($cx: ident, $lint: ident, $deps: expr) => {{

View File

@@ -2,7 +2,8 @@ use std::borrow::Cow;
use std::ops::Range; use std::ops::Range;
use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then}; use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, MacCall, StrLit, StrStyle}; use if_chain::if_chain;
use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, LitKind, MacCall, StrLit, StrStyle};
use rustc_ast::token; use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream; use rustc_ast::tokenstream::TokenStream;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@@ -437,7 +438,7 @@ impl Write {
return (Some(fmtstr), None); return (Some(fmtstr), None);
}; };
match &token_expr.kind { match &token_expr.kind {
ExprKind::Lit(_) => { ExprKind::Lit(lit) if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => {
let mut all_simple = true; let mut all_simple = true;
let mut seen = false; let mut seen = false;
for arg in &args { for arg in &args {
@@ -457,8 +458,11 @@ impl Write {
idx += 1; idx += 1;
}, },
ExprKind::Assign(lhs, rhs, _) => { ExprKind::Assign(lhs, rhs, _) => {
if let ExprKind::Lit(_) = rhs.kind { if_chain! {
if let ExprKind::Path(_, p) = &lhs.kind { if let ExprKind::Lit(ref lit) = rhs.kind;
if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..));
if let ExprKind::Path(_, p) = &lhs.kind;
then {
let mut all_simple = true; let mut all_simple = true;
let mut seen = false; let mut seen = false;
for arg in &args { for arg in &args {

View File

@@ -6,7 +6,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_target::abi::LayoutOf as _; use rustc_target::abi::LayoutOf as _;
use rustc_typeck::hir_ty_to_ty; use rustc_typeck::hir_ty_to_ty;
use crate::utils::{is_type_diagnostic_item, match_type, paths, span_lint_and_help}; use crate::utils::{is_normalizable, is_type_diagnostic_item, match_type, paths, span_lint_and_help};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for maps with zero-sized value types anywhere in the code. /// **What it does:** Checks for maps with zero-sized value types anywhere in the code.
@@ -50,6 +50,8 @@ impl LateLintPass<'_> for ZeroSizedMapValues {
if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP); if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP);
if let Adt(_, ref substs) = ty.kind(); if let Adt(_, ref substs) = ty.kind();
let ty = substs.type_at(1); let ty = substs.type_at(1);
// Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`.
if is_normalizable(cx, cx.param_env, ty);
if let Ok(layout) = cx.layout_of(ty); if let Ok(layout) = cx.layout_of(ty);
if layout.is_zst(); if layout.is_zst();
then { then {

View File

@@ -23,6 +23,7 @@ because that's clearly a non-descriptive name.
- [Running rustfmt](#running-rustfmt) - [Running rustfmt](#running-rustfmt)
- [Debugging](#debugging) - [Debugging](#debugging)
- [PR Checklist](#pr-checklist) - [PR Checklist](#pr-checklist)
- [Adding configuration to a lint](#adding-configuration-to-a-lint)
- [Cheatsheet](#cheatsheet) - [Cheatsheet](#cheatsheet)
## Setup ## Setup
@@ -526,6 +527,81 @@ Before submitting your PR make sure you followed all of the basic requirements:
- \[ ] Added lint documentation - \[ ] Added lint documentation
- \[ ] Run `cargo dev fmt` - \[ ] Run `cargo dev fmt`
## Adding configuration to a lint
Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace
directory. Adding a configuration to a lint can be useful for thresholds or to constrain some
behavior that can be seen as a false positive for some users. Adding a configuration is done
in the following steps:
1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs)
like this:
```rust
/// Lint: LINT_NAME. <The configuration field doc comment>
(configuration_ident, "configuration_value": Type, DefaultValue),
```
The configuration value and identifier should usually be the same. The doc comment will be
automatically added to the lint documentation.
2. Adding the configuration value to the lint impl struct:
1. This first requires the definition of a lint impl struct. Lint impl structs are usually
generated with the `declare_lint_pass!` macro. This struct needs to be defined manually
to add some kind of metadata to it:
```rust
// Generated struct definition
declare_lint_pass!(StructName => [
LINT_NAME
]);
// New manual definition struct
#[derive(Copy, Clone)]
pub struct StructName {}
impl_lint_pass!(StructName => [
LINT_NAME
]);
```
2. Next add the configuration value and a corresponding creation method like this:
```rust
#[derive(Copy, Clone)]
pub struct StructName {
configuration_ident: Type,
}
// ...
impl StructName {
pub fn new(configuration_ident: Type) -> Self {
Self {
configuration_ident,
}
}
}
```
3. Passing the configuration value to the lint impl struct:
First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs).
Make sure that `clippy dev update_lints` added it beforehand. The configuration value is now
cloned or copied into a local value that is then passed to the impl struct like this:
```rust
// Default generated registration:
store.register_late_pass(|| box module::StructName);
// New registration with configuration value
let configuration_ident = conf.configuration_ident.clone();
store.register_late_pass(move || box module::StructName::new(configuration_ident));
```
Congratulations the work is almost done. The configuration value can now be accessed
in the linting code via `self.configuration_ident`.
4. Adding tests:
1. The default configured value can be tested like any normal lint in [`tests/ui`](/tests/ui).
2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml).
Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file
with the configuration value and a rust file that should be linted by clippy. The test can
otherwise be written as usual.
## Cheatsheet ## Cheatsheet
Here are some pointers to things you are likely going to need for every lint: Here are some pointers to things you are likely going to need for every lint:

View File

@@ -11,6 +11,7 @@ the codebase take a look at [Adding Lints] or [Common Tools].
- [Get the Code](#get-the-code) - [Get the Code](#get-the-code)
- [Building and Testing](#building-and-testing) - [Building and Testing](#building-and-testing)
- [`cargo dev`](#cargo-dev) - [`cargo dev`](#cargo-dev)
- [Common Abbreviations](#common-abbreviations)
- [PR](#pr) - [PR](#pr)
## Get the Code ## Get the Code
@@ -94,3 +95,22 @@ cargo dev ra_setup
We follow a rustc no merge-commit policy. We follow a rustc no merge-commit policy.
See <https://rustc-dev-guide.rust-lang.org/contributing.html#opening-a-pr>. See <https://rustc-dev-guide.rust-lang.org/contributing.html#opening-a-pr>.
## Common Abbreviations
| Abbreviation | Meaning |
| ------------ | -------------------------------------- |
| UB | Undefined Behavior |
| FP | False Positive |
| FN | False Negative |
| ICE | Internal Compiler Error |
| AST | Abstract Syntax Tree |
| MIR | Mid-Level Intermediate Representation |
| HIR | High-Level Intermediate Representation |
| TCX | Type context |
This is a concise list of abbreviations that can come up during clippy development. An extensive
general list can be found in the [rustc-dev-guide glossary][glossary]. Always feel free to ask if
an abbreviation or meaning is unclear to you.
[glossary]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html

3372
lintcheck-logs/logs.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,3 @@
[toolchain] [toolchain]
channel = "nightly-2021-01-15" channel = "nightly-2021-01-30"
components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"]

View File

@@ -44,7 +44,9 @@ fn third_party_crates() -> String {
}; };
if let Some(name) = path.file_name().and_then(OsStr::to_str) { if let Some(name) = path.file_name().and_then(OsStr::to_str) {
for dep in CRATES { for dep in CRATES {
if name.starts_with(&format!("lib{}-", dep)) && name.ends_with(".rlib") { if name.starts_with(&format!("lib{}-", dep))
&& name.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rlib")) == Some(true)
{
if let Some(old) = crates.insert(dep, path.clone()) { if let Some(old) = crates.insert(dep, path.clone()) {
panic!("Found multiple rlibs for crate `{}`: `{:?}` and `{:?}", dep, old, path); panic!("Found multiple rlibs for crate `{}`: `{:?}` and `{:?}", dep, old, path);
} }

View File

@@ -18,6 +18,9 @@ mod paths {
// Path with bad module // Path with bad module
pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
// Path to method on an enum inherent impl
pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"];
} }
fn main() {} fn main() {}

View File

@@ -0,0 +1,44 @@
#![warn(clippy::case_sensitive_file_extension_comparisons)]
use std::string::String;
struct TestStruct {}
impl TestStruct {
fn ends_with(self, arg: &str) {}
}
fn is_rust_file(filename: &str) -> bool {
filename.ends_with(".rs")
}
fn main() {
// std::string::String and &str should trigger the lint failure with .ext12
let _ = String::from("").ends_with(".ext12");
let _ = "str".ends_with(".ext12");
// The test struct should not trigger the lint failure with .ext12
TestStruct {}.ends_with(".ext12");
// std::string::String and &str should trigger the lint failure with .EXT12
let _ = String::from("").ends_with(".EXT12");
let _ = "str".ends_with(".EXT12");
// The test struct should not trigger the lint failure with .EXT12
TestStruct {}.ends_with(".EXT12");
// Should not trigger the lint failure with .eXT12
let _ = String::from("").ends_with(".eXT12");
let _ = "str".ends_with(".eXT12");
TestStruct {}.ends_with(".eXT12");
// Should not trigger the lint failure with .EXT123 (too long)
let _ = String::from("").ends_with(".EXT123");
let _ = "str".ends_with(".EXT123");
TestStruct {}.ends_with(".EXT123");
// Shouldn't fail if it doesn't start with a dot
let _ = String::from("").ends_with("a.ext");
let _ = "str".ends_with("a.extA");
TestStruct {}.ends_with("a.ext");
}

View File

@@ -0,0 +1,43 @@
error: case-sensitive file extension comparison
--> $DIR/case_sensitive_file_extension_comparisons.rs:12:14
|
LL | filename.ends_with(".rs")
| ^^^^^^^^^^^^^^^^
|
= note: `-D clippy::case-sensitive-file-extension-comparisons` implied by `-D warnings`
= help: consider using a case-insensitive comparison instead
error: case-sensitive file extension comparison
--> $DIR/case_sensitive_file_extension_comparisons.rs:17:30
|
LL | let _ = String::from("").ends_with(".ext12");
| ^^^^^^^^^^^^^^^^^^^
|
= help: consider using a case-insensitive comparison instead
error: case-sensitive file extension comparison
--> $DIR/case_sensitive_file_extension_comparisons.rs:18:19
|
LL | let _ = "str".ends_with(".ext12");
| ^^^^^^^^^^^^^^^^^^^
|
= help: consider using a case-insensitive comparison instead
error: case-sensitive file extension comparison
--> $DIR/case_sensitive_file_extension_comparisons.rs:24:30
|
LL | let _ = String::from("").ends_with(".EXT12");
| ^^^^^^^^^^^^^^^^^^^
|
= help: consider using a case-insensitive comparison instead
error: case-sensitive file extension comparison
--> $DIR/case_sensitive_file_extension_comparisons.rs:25:19
|
LL | let _ = "str".ends_with(".EXT12");
| ^^^^^^^^^^^^^^^^^^^
|
= help: consider using a case-insensitive comparison instead
error: aborting due to 5 previous errors

View File

@@ -40,6 +40,35 @@ fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>
// there is still a better way to write this. // there is still a better way to write this.
mac!(res_opt => Ok(val), val => Some(n), foo(n)); mac!(res_opt => Ok(val), val => Some(n), foo(n));
} }
// deref reference value
match Some(&[1]) {
Some(s) => match *s {
[n] => foo(n),
_ => (),
},
_ => (),
}
// ref pattern and deref
match Some(&[1]) {
Some(ref s) => match &*s {
[n] => foo(n),
_ => (),
},
_ => (),
}
}
fn no_lint() {
// deref inner value (cannot pattern match with Vec)
match Some(vec![1]) {
Some(s) => match *s {
[n] => foo(n),
_ => (),
},
_ => (),
}
} }
fn make<T>() -> T { fn make<T>() -> T {

View File

@@ -57,5 +57,41 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n));
| Replace this binding | Replace this binding
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 3 previous errors error: Unnecessary nested match
--> $DIR/collapsible_match2.rs:46:20
|
LL | Some(s) => match *s {
| ____________________^
LL | | [n] => foo(n),
LL | | _ => (),
LL | | },
| |_________^
|
help: The outer pattern can be modified to include the inner pattern.
--> $DIR/collapsible_match2.rs:46:14
|
LL | Some(s) => match *s {
| ^ Replace this binding
LL | [n] => foo(n),
| ^^^ with this pattern
error: Unnecessary nested match
--> $DIR/collapsible_match2.rs:55:24
|
LL | Some(ref s) => match &*s {
| ________________________^
LL | | [n] => foo(n),
LL | | _ => (),
LL | | },
| |_________^
|
help: The outer pattern can be modified to include the inner pattern.
--> $DIR/collapsible_match2.rs:55:14
|
LL | Some(ref s) => match &*s {
| ^^^^^ Replace this binding
LL | [n] => foo(n),
| ^^^ with this pattern
error: aborting due to 5 previous errors

View File

@@ -11,7 +11,7 @@ struct S {
f: Vec<Vec<Box<(u32, u32, u32, u32)>>>, f: Vec<Vec<Box<(u32, u32, u32, u32)>>>,
} }
struct TS(Vec<Vec<Box<(u32, u32, u32, u32)>>>); struct Ts(Vec<Vec<Box<(u32, u32, u32, u32)>>>);
enum E { enum E {
Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>), Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>),

View File

@@ -21,7 +21,7 @@ LL | f: Vec<Vec<Box<(u32, u32, u32, u32)>>>,
error: very complex type used. Consider factoring parts into `type` definitions error: very complex type used. Consider factoring parts into `type` definitions
--> $DIR/complex_types.rs:14:11 --> $DIR/complex_types.rs:14:11
| |
LL | struct TS(Vec<Vec<Box<(u32, u32, u32, u32)>>>); LL | struct Ts(Vec<Vec<Box<(u32, u32, u32, u32)>>>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: very complex type used. Consider factoring parts into `type` definitions error: very complex type used. Consider factoring parts into `type` definitions

View File

@@ -1,5 +1,6 @@
// originally from rustc ./src/test/ui/regions/issue-78262.rs // originally from rustc ./src/test/ui/regions/issue-78262.rs
// ICE: to get the signature of a closure, use substs.as_closure().sig() not fn_sig() // ICE: to get the signature of a closure, use substs.as_closure().sig() not fn_sig()
#![allow(clippy::upper_case_acronyms)]
trait TT {} trait TT {}

View File

@@ -1,13 +1,13 @@
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/ice-6256.rs:11:28 --> $DIR/ice-6256.rs:12:28
| |
LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types
| ^^^^ lifetime mismatch | ^^^^ lifetime mismatch
| |
= note: expected reference `&(dyn TT + 'static)` = note: expected reference `&(dyn TT + 'static)`
found reference `&dyn TT` found reference `&dyn TT`
note: the anonymous lifetime #1 defined on the body at 11:13... note: the anonymous lifetime #1 defined on the body at 12:13...
--> $DIR/ice-6256.rs:11:13 --> $DIR/ice-6256.rs:12:13
| |
LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^

View File

@@ -0,0 +1,16 @@
// The test for the ICE 6539: https://github.com/rust-lang/rust-clippy/issues/6539.
// The cause is that `zero_sized_map_values` used `layout_of` with types from type aliases,
// which is essentially the same as the ICE 4968.
// Note that only type aliases with associated types caused the crash this time,
// not others such as trait impls.
use std::collections::{BTreeMap, HashMap};
pub trait Trait {
type Assoc;
}
type TypeAlias<T> = HashMap<(), <T as Trait>::Assoc>;
type TypeAlias2<T> = BTreeMap<(), <T as Trait>::Assoc>;
fn main() {}

View File

@@ -10,5 +10,6 @@
#[warn(clippy::temporary_cstring_as_ptr)] #[warn(clippy::temporary_cstring_as_ptr)]
#[warn(clippy::panic_params)] #[warn(clippy::panic_params)]
#[warn(clippy::unknown_clippy_lints)] #[warn(clippy::unknown_clippy_lints)]
#[warn(clippy::find_map)]
fn main() {} fn main() {}

View File

@@ -72,11 +72,17 @@ error: lint `clippy::unknown_clippy_lints` has been removed: this lint has been
LL | #[warn(clippy::unknown_clippy_lints)] LL | #[warn(clippy::unknown_clippy_lints)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
--> $DIR/deprecated.rs:13:8
|
LL | #[warn(clippy::find_map)]
| ^^^^^^^^^^^^^^^^
error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7 error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
--> $DIR/deprecated.rs:1:8 --> $DIR/deprecated.rs:1:8
| |
LL | #[warn(clippy::unstable_as_slice)] LL | #[warn(clippy::unstable_as_slice)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 13 previous errors error: aborting due to 14 previous errors

View File

@@ -53,6 +53,7 @@ fn test_units() {
/// DirectX /// DirectX
/// ECMAScript /// ECMAScript
/// OAuth GraphQL /// OAuth GraphQL
/// WebGL
/// TeX LaTeX BibTeX BibLaTeX /// TeX LaTeX BibTeX BibLaTeX
/// CamelCase (see also #2395) /// CamelCase (see also #2395)
/// be_sure_we_got_to_the_end_of_it /// be_sure_we_got_to_the_end_of_it

View File

@@ -55,133 +55,133 @@ LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:58:5 --> $DIR/doc.rs:59:5
| |
LL | /// be_sure_we_got_to_the_end_of_it LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `link_with_underscores` between ticks in the documentation error: you should put `link_with_underscores` between ticks in the documentation
--> $DIR/doc.rs:62:22 --> $DIR/doc.rs:63:22
| |
LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823.
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
error: you should put `inline_link2` between ticks in the documentation error: you should put `inline_link2` between ticks in the documentation
--> $DIR/doc.rs:65:21 --> $DIR/doc.rs:66:21
| |
LL | /// It can also be [inline_link2]. LL | /// It can also be [inline_link2].
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:75:5 --> $DIR/doc.rs:76:5
| |
LL | /// be_sure_we_got_to_the_end_of_it LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `CamelCaseThing` between ticks in the documentation error: you should put `CamelCaseThing` between ticks in the documentation
--> $DIR/doc.rs:83:8 --> $DIR/doc.rs:84:8
| |
LL | /// ## CamelCaseThing LL | /// ## CamelCaseThing
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: you should put `CamelCaseThing` between ticks in the documentation error: you should put `CamelCaseThing` between ticks in the documentation
--> $DIR/doc.rs:86:7 --> $DIR/doc.rs:87:7
| |
LL | /// # CamelCaseThing LL | /// # CamelCaseThing
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: you should put `CamelCaseThing` between ticks in the documentation error: you should put `CamelCaseThing` between ticks in the documentation
--> $DIR/doc.rs:88:22 --> $DIR/doc.rs:89:22
| |
LL | /// Not a title #897 CamelCaseThing LL | /// Not a title #897 CamelCaseThing
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:89:5 --> $DIR/doc.rs:90:5
| |
LL | /// be_sure_we_got_to_the_end_of_it LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:96:5 --> $DIR/doc.rs:97:5
| |
LL | /// be_sure_we_got_to_the_end_of_it LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:109:5 --> $DIR/doc.rs:110:5
| |
LL | /// be_sure_we_got_to_the_end_of_it LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `FooBar` between ticks in the documentation error: you should put `FooBar` between ticks in the documentation
--> $DIR/doc.rs:120:43 --> $DIR/doc.rs:121:43
| |
LL | /** E.g., serialization of an empty list: FooBar LL | /** E.g., serialization of an empty list: FooBar
| ^^^^^^ | ^^^^^^
error: you should put `BarQuz` between ticks in the documentation error: you should put `BarQuz` between ticks in the documentation
--> $DIR/doc.rs:125:5 --> $DIR/doc.rs:126:5
| |
LL | And BarQuz too. LL | And BarQuz too.
| ^^^^^^ | ^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:126:1 --> $DIR/doc.rs:127:1
| |
LL | be_sure_we_got_to_the_end_of_it LL | be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `FooBar` between ticks in the documentation error: you should put `FooBar` between ticks in the documentation
--> $DIR/doc.rs:131:43 --> $DIR/doc.rs:132:43
| |
LL | /** E.g., serialization of an empty list: FooBar LL | /** E.g., serialization of an empty list: FooBar
| ^^^^^^ | ^^^^^^
error: you should put `BarQuz` between ticks in the documentation error: you should put `BarQuz` between ticks in the documentation
--> $DIR/doc.rs:136:5 --> $DIR/doc.rs:137:5
| |
LL | And BarQuz too. LL | And BarQuz too.
| ^^^^^^ | ^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:137:1 --> $DIR/doc.rs:138:1
| |
LL | be_sure_we_got_to_the_end_of_it LL | be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
--> $DIR/doc.rs:148:5 --> $DIR/doc.rs:149:5
| |
LL | /// be_sure_we_got_to_the_end_of_it LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put bare URLs between `<`/`>` or make a proper Markdown link error: you should put bare URLs between `<`/`>` or make a proper Markdown link
--> $DIR/doc.rs:175:13 --> $DIR/doc.rs:176:13
| |
LL | /// Not ok: http://www.unicode.org LL | /// Not ok: http://www.unicode.org
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
error: you should put bare URLs between `<`/`>` or make a proper Markdown link error: you should put bare URLs between `<`/`>` or make a proper Markdown link
--> $DIR/doc.rs:176:13 --> $DIR/doc.rs:177:13
| |
LL | /// Not ok: https://www.unicode.org LL | /// Not ok: https://www.unicode.org
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
error: you should put bare URLs between `<`/`>` or make a proper Markdown link error: you should put bare URLs between `<`/`>` or make a proper Markdown link
--> $DIR/doc.rs:177:13 --> $DIR/doc.rs:178:13
| |
LL | /// Not ok: http://www.unicode.org/ LL | /// Not ok: http://www.unicode.org/
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
error: you should put bare URLs between `<`/`>` or make a proper Markdown link error: you should put bare URLs between `<`/`>` or make a proper Markdown link
--> $DIR/doc.rs:178:13 --> $DIR/doc.rs:179:13
| |
LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `mycrate::Collection` between ticks in the documentation error: you should put `mycrate::Collection` between ticks in the documentation
--> $DIR/doc.rs:181:22 --> $DIR/doc.rs:182:22
| |
LL | /// An iterator over mycrate::Collection's values. LL | /// An iterator over mycrate::Collection's values.
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^

View File

@@ -1,6 +1,6 @@
#![feature(non_ascii_idents)] #![feature(non_ascii_idents)]
#![warn(clippy::enum_variant_names, clippy::pub_enum_variant_names)] #![warn(clippy::enum_variant_names, clippy::pub_enum_variant_names)]
#![allow(non_camel_case_types)] #![allow(non_camel_case_types, clippy::upper_case_acronyms)]
enum FakeCallType { enum FakeCallType {
CALL, CALL,

View File

@@ -0,0 +1,82 @@
// run-rustfix
#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
#![allow(unused)]
fn main() {
// nop
}
pub mod enums {
#[non_exhaustive]
pub enum Exhaustive {
Foo,
Bar,
Baz,
Quux(String),
}
/// Some docs
#[repr(C)]
#[non_exhaustive]
pub enum ExhaustiveWithAttrs {
Foo,
Bar,
Baz,
Quux(String),
}
// no warning, already non_exhaustive
#[non_exhaustive]
pub enum NonExhaustive {
Foo,
Bar,
Baz,
Quux(String),
}
// no warning, private
enum ExhaustivePrivate {
Foo,
Bar,
Baz,
Quux(String),
}
// no warning, private
#[non_exhaustive]
enum NonExhaustivePrivate {
Foo,
Bar,
Baz,
Quux(String),
}
}
pub mod structs {
#[non_exhaustive]
pub struct Exhaustive {
foo: u8,
bar: String,
}
// no warning, already non_exhaustive
#[non_exhaustive]
pub struct NonExhaustive {
foo: u8,
bar: String,
}
// no warning, private
struct ExhaustivePrivate {
foo: u8,
bar: String,
}
// no warning, private
#[non_exhaustive]
struct NonExhaustivePrivate {
foo: u8,
bar: String,
}
}

View File

@@ -0,0 +1,79 @@
// run-rustfix
#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
#![allow(unused)]
fn main() {
// nop
}
pub mod enums {
pub enum Exhaustive {
Foo,
Bar,
Baz,
Quux(String),
}
/// Some docs
#[repr(C)]
pub enum ExhaustiveWithAttrs {
Foo,
Bar,
Baz,
Quux(String),
}
// no warning, already non_exhaustive
#[non_exhaustive]
pub enum NonExhaustive {
Foo,
Bar,
Baz,
Quux(String),
}
// no warning, private
enum ExhaustivePrivate {
Foo,
Bar,
Baz,
Quux(String),
}
// no warning, private
#[non_exhaustive]
enum NonExhaustivePrivate {
Foo,
Bar,
Baz,
Quux(String),
}
}
pub mod structs {
pub struct Exhaustive {
foo: u8,
bar: String,
}
// no warning, already non_exhaustive
#[non_exhaustive]
pub struct NonExhaustive {
foo: u8,
bar: String,
}
// no warning, private
struct ExhaustivePrivate {
foo: u8,
bar: String,
}
// no warning, private
#[non_exhaustive]
struct NonExhaustivePrivate {
foo: u8,
bar: String,
}
}

View File

@@ -0,0 +1,61 @@
error: exported enums should not be exhaustive
--> $DIR/exhaustive_items.rs:11:5
|
LL | / pub enum Exhaustive {
LL | | Foo,
LL | | Bar,
LL | | Baz,
LL | | Quux(String),
LL | | }
| |_____^
|
note: the lint level is defined here
--> $DIR/exhaustive_items.rs:3:9
|
LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
help: try adding #[non_exhaustive]
|
LL | #[non_exhaustive]
LL | pub enum Exhaustive {
|
error: exported enums should not be exhaustive
--> $DIR/exhaustive_items.rs:20:5
|
LL | / pub enum ExhaustiveWithAttrs {
LL | | Foo,
LL | | Bar,
LL | | Baz,
LL | | Quux(String),
LL | | }
| |_____^
|
help: try adding #[non_exhaustive]
|
LL | #[non_exhaustive]
LL | pub enum ExhaustiveWithAttrs {
|
error: exported structs should not be exhaustive
--> $DIR/exhaustive_items.rs:55:5
|
LL | / pub struct Exhaustive {
LL | | foo: u8,
LL | | bar: String,
LL | | }
| |_____^
|
note: the lint level is defined here
--> $DIR/exhaustive_items.rs:3:35
|
LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
help: try adding #[non_exhaustive]
|
LL | #[non_exhaustive]
LL | pub struct Exhaustive {
|
error: aborting due to 3 previous errors

View File

@@ -1,12 +1,3 @@
error: called `filter(..).map(..)` on an `Iterator`
--> $DIR/filter_methods.rs:6:21
|
LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::filter-map` implied by `-D warnings`
= help: this is more succinctly expressed by calling `.filter_map(..)` instead
error: called `filter(..).flat_map(..)` on an `Iterator` error: called `filter(..).flat_map(..)` on an `Iterator`
--> $DIR/filter_methods.rs:8:21 --> $DIR/filter_methods.rs:8:21
| |
@@ -17,6 +8,7 @@ LL | | .filter(|&x| x == 0)
LL | | .flat_map(|x| x.checked_mul(2)) LL | | .flat_map(|x| x.checked_mul(2))
| |_______________________________________^ | |_______________________________________^
| |
= note: `-D clippy::filter-map` implied by `-D warnings`
= help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
error: called `filter_map(..).flat_map(..)` on an `Iterator` error: called `filter_map(..).flat_map(..)` on an `Iterator`
@@ -43,5 +35,5 @@ LL | | .map(|x| x.checked_mul(2))
| |
= help: this is more succinctly expressed by only calling `.filter_map(..)` instead = help: this is more succinctly expressed by only calling `.filter_map(..)` instead
error: aborting due to 4 previous errors error: aborting due to 3 previous errors

View File

@@ -1,26 +0,0 @@
error: called `find(..).map(..)` on an `Iterator`
--> $DIR/find_map.rs:20:26
|
LL | let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::find-map` implied by `-D warnings`
= help: this is more succinctly expressed by calling `.find_map(..)` instead
error: called `find(..).map(..)` on an `Iterator`
--> $DIR/find_map.rs:23:29
|
LL | let _: Option<Flavor> = desserts_of_the_week
| _____________________________^
LL | | .iter()
LL | | .find(|dessert| match *dessert {
LL | | Dessert::Cake(_) => true,
... |
LL | | _ => unreachable!(),
LL | | });
| |__________^
|
= help: this is more succinctly expressed by calling `.find_map(..)` instead
error: aborting due to 2 previous errors

View File

@@ -0,0 +1,22 @@
#![warn(clippy::inspect_for_each)]
fn main() {
let a: Vec<usize> = vec![1, 2, 3, 4, 5];
let mut b: Vec<usize> = Vec::new();
a.into_iter().inspect(|x| assert!(*x > 0)).for_each(|x| {
let y = do_some(x);
let z = do_more(y);
b.push(z);
});
assert_eq!(b, vec![4, 5, 6, 7, 8]);
}
fn do_some(a: usize) -> usize {
a + 1
}
fn do_more(a: usize) -> usize {
a + 2
}

View File

@@ -0,0 +1,16 @@
error: called `inspect(..).for_each(..)` on an `Iterator`
--> $DIR/inspect_for_each.rs:7:19
|
LL | a.into_iter().inspect(|x| assert!(*x > 0)).for_each(|x| {
| ___________________^
LL | | let y = do_some(x);
LL | | let z = do_more(y);
LL | | b.push(z);
LL | | });
| |______^
|
= note: `-D clippy::inspect-for-each` implied by `-D warnings`
= help: move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`
error: aborting due to previous error

View File

@@ -0,0 +1,37 @@
// run-rustfix
#![allow(dead_code)]
#![warn(clippy::manual_filter_map)]
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
fn main() {
// is_some(), unwrap()
let _ = (0..).filter_map(|a| to_opt(a));
// ref pattern, expect()
let _ = (0..).filter_map(|a| to_opt(a));
// is_ok(), unwrap_or()
let _ = (0..).filter_map(|a| to_res(a).ok());
}
fn no_lint() {
// no shared code
let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
// very close but different since filter() provides a reference
let _ = (0..).filter(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
// similar but different
let _ = (0..).filter(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
let _ = (0..)
.filter(|n| to_opt(n).map(|n| n + 1).is_some())
.map(|a| to_opt(a).unwrap());
}
fn to_opt<T>(_: T) -> Option<T> {
unimplemented!()
}
fn to_res<T>(_: T) -> Result<T, ()> {
unimplemented!()
}

View File

@@ -0,0 +1,37 @@
// run-rustfix
#![allow(dead_code)]
#![warn(clippy::manual_filter_map)]
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
fn main() {
// is_some(), unwrap()
let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
// ref pattern, expect()
let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
// is_ok(), unwrap_or()
let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
}
fn no_lint() {
// no shared code
let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
// very close but different since filter() provides a reference
let _ = (0..).filter(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
// similar but different
let _ = (0..).filter(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
let _ = (0..)
.filter(|n| to_opt(n).map(|n| n + 1).is_some())
.map(|a| to_opt(a).unwrap());
}
fn to_opt<T>(_: T) -> Option<T> {
unimplemented!()
}
fn to_res<T>(_: T) -> Result<T, ()> {
unimplemented!()
}

View File

@@ -0,0 +1,22 @@
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
--> $DIR/manual_filter_map.rs:8:19
|
LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))`
|
= note: `-D clippy::manual-filter-map` implied by `-D warnings`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
--> $DIR/manual_filter_map.rs:11:19
|
LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
--> $DIR/manual_filter_map.rs:14:19
|
LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())`
error: aborting due to 3 previous errors

View File

@@ -0,0 +1,37 @@
// run-rustfix
#![allow(dead_code)]
#![warn(clippy::manual_find_map)]
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
fn main() {
// is_some(), unwrap()
let _ = (0..).find_map(|a| to_opt(a));
// ref pattern, expect()
let _ = (0..).find_map(|a| to_opt(a));
// is_ok(), unwrap_or()
let _ = (0..).find_map(|a| to_res(a).ok());
}
fn no_lint() {
// no shared code
let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
// very close but different since filter() provides a reference
let _ = (0..).find(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
// similar but different
let _ = (0..).find(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
let _ = (0..)
.find(|n| to_opt(n).map(|n| n + 1).is_some())
.map(|a| to_opt(a).unwrap());
}
fn to_opt<T>(_: T) -> Option<T> {
unimplemented!()
}
fn to_res<T>(_: T) -> Result<T, ()> {
unimplemented!()
}

View File

@@ -0,0 +1,37 @@
// run-rustfix
#![allow(dead_code)]
#![warn(clippy::manual_find_map)]
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
fn main() {
// is_some(), unwrap()
let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
// ref pattern, expect()
let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
// is_ok(), unwrap_or()
let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
}
fn no_lint() {
// no shared code
let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
// very close but different since filter() provides a reference
let _ = (0..).find(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
// similar but different
let _ = (0..).find(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
let _ = (0..)
.find(|n| to_opt(n).map(|n| n + 1).is_some())
.map(|a| to_opt(a).unwrap());
}
fn to_opt<T>(_: T) -> Option<T> {
unimplemented!()
}
fn to_res<T>(_: T) -> Result<T, ()> {
unimplemented!()
}

View File

@@ -0,0 +1,22 @@
error: `find(..).map(..)` can be simplified as `find_map(..)`
--> $DIR/manual_find_map.rs:8:19
|
LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))`
|
= note: `-D clippy::manual-find-map` implied by `-D warnings`
error: `find(..).map(..)` can be simplified as `find_map(..)`
--> $DIR/manual_find_map.rs:11:19
|
LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))`
error: `find(..).map(..)` can be simplified as `find_map(..)`
--> $DIR/manual_find_map.rs:14:19
|
LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())`
error: aborting due to 3 previous errors

View File

@@ -99,4 +99,51 @@ fn main() {
_ => false, _ => false,
}; };
} }
{
// should print "z" in suggestion (#6503)
let z = &Some(3);
let _z = matches!(z, Some(3));
}
{
// this could also print "z" in suggestion..?
let z = Some(3);
let _z = matches!(&z, Some(3));
}
{
enum AnEnum {
X,
Y,
}
fn foo(_x: AnEnum) {}
fn main() {
let z = AnEnum::X;
// we can't remove the reference here!
let _ = matches!(&z, AnEnum::X);
foo(z);
}
}
{
struct S(i32);
fn fun(_val: Option<S>) {}
let val = Some(S(42));
// we need the reference here because later val is consumed by fun()
let _res = matches!(&val, &Some(ref _a));
fun(val);
}
{
struct S(i32);
fn fun(_val: Option<S>) {}
let val = Some(S(42));
let _res = matches!(&val, &Some(ref _a));
fun(val);
}
} }

View File

@@ -119,4 +119,66 @@ fn main() {
_ => false, _ => false,
}; };
} }
{
// should print "z" in suggestion (#6503)
let z = &Some(3);
let _z = match &z {
Some(3) => true,
_ => false,
};
}
{
// this could also print "z" in suggestion..?
let z = Some(3);
let _z = match &z {
Some(3) => true,
_ => false,
};
}
{
enum AnEnum {
X,
Y,
}
fn foo(_x: AnEnum) {}
fn main() {
let z = AnEnum::X;
// we can't remove the reference here!
let _ = match &z {
AnEnum::X => true,
_ => false,
};
foo(z);
}
}
{
struct S(i32);
fn fun(_val: Option<S>) {}
let val = Some(S(42));
// we need the reference here because later val is consumed by fun()
let _res = match &val {
&Some(ref _a) => true,
_ => false,
};
fun(val);
}
{
struct S(i32);
fn fun(_val: Option<S>) {}
let val = Some(S(42));
let _res = match &val {
&Some(ref _a) => true,
_ => false,
};
fun(val);
}
} }

View File

@@ -70,5 +70,88 @@ LL | | _ => true,
LL | | }; LL | | };
| |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
error: aborting due to 7 previous errors error: match expression looks like `matches!` macro
--> $DIR/match_expr_like_matches_macro.rs:126:18
|
LL | let _z = match &z {
| __________________^
LL | | Some(3) => true,
LL | | _ => false,
LL | | };
| |_________^ help: try this: `matches!(z, Some(3))`
error: match expression looks like `matches!` macro
--> $DIR/match_expr_like_matches_macro.rs:135:18
|
LL | let _z = match &z {
| __________________^
LL | | Some(3) => true,
LL | | _ => false,
LL | | };
| |_________^ help: try this: `matches!(&z, Some(3))`
error: match expression looks like `matches!` macro
--> $DIR/match_expr_like_matches_macro.rs:152:21
|
LL | let _ = match &z {
| _____________________^
LL | | AnEnum::X => true,
LL | | _ => false,
LL | | };
| |_____________^ help: try this: `matches!(&z, AnEnum::X)`
error: match expression looks like `matches!` macro
--> $DIR/match_expr_like_matches_macro.rs:166:20
|
LL | let _res = match &val {
| ____________________^
LL | | &Some(ref _a) => true,
LL | | _ => false,
LL | | };
| |_________^ help: try this: `matches!(&val, &Some(ref _a))`
error: you don't need to add `&` to both the expression and the patterns
--> $DIR/match_expr_like_matches_macro.rs:166:20
|
LL | let _res = match &val {
| ____________________^
LL | | &Some(ref _a) => true,
LL | | _ => false,
LL | | };
| |_________^
|
= note: `-D clippy::match-ref-pats` implied by `-D warnings`
help: try
|
LL | let _res = match val {
LL | Some(ref _a) => true,
|
error: match expression looks like `matches!` macro
--> $DIR/match_expr_like_matches_macro.rs:178:20
|
LL | let _res = match &val {
| ____________________^
LL | | &Some(ref _a) => true,
LL | | _ => false,
LL | | };
| |_________^ help: try this: `matches!(&val, &Some(ref _a))`
error: you don't need to add `&` to both the expression and the patterns
--> $DIR/match_expr_like_matches_macro.rs:178:20
|
LL | let _res = match &val {
| ____________________^
LL | | &Some(ref _a) => true,
LL | | _ => false,
LL | | };
| |_________^
|
help: try
|
LL | let _res = match val {
LL | Some(ref _a) => true,
|
error: aborting due to 14 previous errors

View File

@@ -1,7 +1,13 @@
// run-rustfix // run-rustfix
#![warn(clippy::needless_question_mark)] #![warn(clippy::needless_question_mark)]
#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] #![allow(
clippy::needless_return,
clippy::unnecessary_unwrap,
clippy::upper_case_acronyms,
dead_code,
unused_must_use
)]
#![feature(custom_inner_attributes)] #![feature(custom_inner_attributes)]
struct TO { struct TO {

View File

@@ -1,7 +1,13 @@
// run-rustfix // run-rustfix
#![warn(clippy::needless_question_mark)] #![warn(clippy::needless_question_mark)]
#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] #![allow(
clippy::needless_return,
clippy::unnecessary_unwrap,
clippy::upper_case_acronyms,
dead_code,
unused_must_use
)]
#![feature(custom_inner_attributes)] #![feature(custom_inner_attributes)]
struct TO { struct TO {

View File

@@ -1,5 +1,5 @@
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:17:12 --> $DIR/needless_question_mark.rs:23:12
| |
LL | return Some(to.magic?); LL | return Some(to.magic?);
| ^^^^^^^^^^^^^^^ help: try: `to.magic` | ^^^^^^^^^^^^^^^ help: try: `to.magic`
@@ -7,79 +7,79 @@ LL | return Some(to.magic?);
= note: `-D clippy::needless-question-mark` implied by `-D warnings` = note: `-D clippy::needless-question-mark` implied by `-D warnings`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:25:12 --> $DIR/needless_question_mark.rs:31:12
| |
LL | return Some(to.magic?) LL | return Some(to.magic?)
| ^^^^^^^^^^^^^^^ help: try: `to.magic` | ^^^^^^^^^^^^^^^ help: try: `to.magic`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:30:5 --> $DIR/needless_question_mark.rs:36:5
| |
LL | Some(to.magic?) LL | Some(to.magic?)
| ^^^^^^^^^^^^^^^ help: try: `to.magic` | ^^^^^^^^^^^^^^^ help: try: `to.magic`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:35:21 --> $DIR/needless_question_mark.rs:41:21
| |
LL | to.and_then(|t| Some(t.magic?)) LL | to.and_then(|t| Some(t.magic?))
| ^^^^^^^^^^^^^^ help: try: `t.magic` | ^^^^^^^^^^^^^^ help: try: `t.magic`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:44:9 --> $DIR/needless_question_mark.rs:50:9
| |
LL | Some(t.magic?) LL | Some(t.magic?)
| ^^^^^^^^^^^^^^ help: try: `t.magic` | ^^^^^^^^^^^^^^ help: try: `t.magic`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:49:12 --> $DIR/needless_question_mark.rs:55:12
| |
LL | return Ok(tr.magic?); LL | return Ok(tr.magic?);
| ^^^^^^^^^^^^^ help: try: `tr.magic` | ^^^^^^^^^^^^^ help: try: `tr.magic`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:56:12 --> $DIR/needless_question_mark.rs:62:12
| |
LL | return Ok(tr.magic?) LL | return Ok(tr.magic?)
| ^^^^^^^^^^^^^ help: try: `tr.magic` | ^^^^^^^^^^^^^ help: try: `tr.magic`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:60:5 --> $DIR/needless_question_mark.rs:66:5
| |
LL | Ok(tr.magic?) LL | Ok(tr.magic?)
| ^^^^^^^^^^^^^ help: try: `tr.magic` | ^^^^^^^^^^^^^ help: try: `tr.magic`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:64:21 --> $DIR/needless_question_mark.rs:70:21
| |
LL | tr.and_then(|t| Ok(t.magic?)) LL | tr.and_then(|t| Ok(t.magic?))
| ^^^^^^^^^^^^ help: try: `t.magic` | ^^^^^^^^^^^^ help: try: `t.magic`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:72:9 --> $DIR/needless_question_mark.rs:78:9
| |
LL | Ok(t.magic?) LL | Ok(t.magic?)
| ^^^^^^^^^^^^ help: try: `t.magic` | ^^^^^^^^^^^^ help: try: `t.magic`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:79:16 --> $DIR/needless_question_mark.rs:85:16
| |
LL | return Ok(t.magic?); LL | return Ok(t.magic?);
| ^^^^^^^^^^^^ help: try: `t.magic` | ^^^^^^^^^^^^ help: try: `t.magic`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:132:9 --> $DIR/needless_question_mark.rs:138:9
| |
LL | Ok(to.magic?) // should be triggered LL | Ok(to.magic?) // should be triggered
| ^^^^^^^^^^^^^ help: try: `to.magic` | ^^^^^^^^^^^^^ help: try: `to.magic`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:148:9 --> $DIR/needless_question_mark.rs:154:9
| |
LL | Some(to.magic?) // should be triggered LL | Some(to.magic?) // should be triggered
| ^^^^^^^^^^^^^^^ help: try: `to.magic` | ^^^^^^^^^^^^^^^ help: try: `to.magic`
error: Question mark operator is useless here error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:156:9 --> $DIR/needless_question_mark.rs:162:9
| |
LL | Ok(to.magic?) // should be triggered LL | Ok(to.magic?) // should be triggered
| ^^^^^^^^^^^^^ help: try: `to.magic` | ^^^^^^^^^^^^^ help: try: `to.magic`

View File

@@ -101,6 +101,25 @@ fn test_return_in_macro() {
needed_return!(0); needed_return!(0);
} }
mod issue6501 {
fn foo(bar: Result<(), ()>) {
bar.unwrap_or_else(|_| {})
}
fn test_closure() {
let _ = || {
};
let _ = || {};
}
struct Foo;
#[allow(clippy::unnecessary_lazy_evaluations)]
fn bar(res: Result<Foo, u8>) -> Foo {
res.unwrap_or_else(|_| Foo)
}
}
fn main() { fn main() {
let _ = test_end_of_fn(); let _ = test_end_of_fn();
let _ = test_no_semicolon(); let _ = test_no_semicolon();

View File

@@ -101,6 +101,25 @@ fn test_return_in_macro() {
needed_return!(0); needed_return!(0);
} }
mod issue6501 {
fn foo(bar: Result<(), ()>) {
bar.unwrap_or_else(|_| return)
}
fn test_closure() {
let _ = || {
return;
};
let _ = || return;
}
struct Foo;
#[allow(clippy::unnecessary_lazy_evaluations)]
fn bar(res: Result<Foo, u8>) -> Foo {
res.unwrap_or_else(|_| return Foo)
}
}
fn main() { fn main() {
let _ = test_end_of_fn(); let _ = test_end_of_fn();
let _ = test_no_semicolon(); let _ = test_no_semicolon();

View File

@@ -84,5 +84,29 @@ error: unneeded `return` statement
LL | return String::new(); LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
error: aborting due to 14 previous errors error: unneeded `return` statement
--> $DIR/needless_return.rs:106:32
|
LL | bar.unwrap_or_else(|_| return)
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
--> $DIR/needless_return.rs:111:13
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
--> $DIR/needless_return.rs:113:20
|
LL | let _ = || return;
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
--> $DIR/needless_return.rs:119:32
|
LL | res.unwrap_or_else(|_| return Foo)
| ^^^^^^^^^^ help: remove `return`: `Foo`
error: aborting due to 18 previous errors

View File

@@ -17,14 +17,14 @@ fn main() {
println!("{bar:8} {foo:>8}", foo = "hello", bar = "world"); println!("{bar:8} {foo:>8}", foo = "hello", bar = "world");
println!("{number:>width$}", number = 1, width = 6); println!("{number:>width$}", number = 1, width = 6);
println!("{number:>0width$}", number = 1, width = 6); println!("{number:>0width$}", number = 1, width = 6);
println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
println!("10 / 4 is {}", 2.5);
println!("2 + 1 = {}", 3);
// these should throw warnings // these should throw warnings
println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
print!("Hello {}", "world"); print!("Hello {}", "world");
println!("Hello {} {}", world, "world"); println!("Hello {} {}", world, "world");
println!("Hello {}", "world"); println!("Hello {}", "world");
println!("10 / 4 is {}", 2.5);
println!("2 + 1 = {}", 3);
// positional args don't change the fact // positional args don't change the fact
// that we're using a literal -- this should // that we're using a literal -- this should

View File

@@ -1,41 +1,23 @@
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:22:71 --> $DIR/print_literal.rs:25:24
| |
LL | println!("{} of {:b} people know binary, the other half doesn't", 1, 2); LL | print!("Hello {}", "world");
| ^ | ^^^^^^^
| |
= note: `-D clippy::print-literal` implied by `-D warnings` = note: `-D clippy::print-literal` implied by `-D warnings`
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:23:24 --> $DIR/print_literal.rs:26:36
|
LL | print!("Hello {}", "world");
| ^^^^^^^
error: literal with an empty format string
--> $DIR/print_literal.rs:24:36
| |
LL | println!("Hello {} {}", world, "world"); LL | println!("Hello {} {}", world, "world");
| ^^^^^^^ | ^^^^^^^
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:25:26 --> $DIR/print_literal.rs:27:26
| |
LL | println!("Hello {}", "world"); LL | println!("Hello {}", "world");
| ^^^^^^^ | ^^^^^^^
error: literal with an empty format string
--> $DIR/print_literal.rs:26:30
|
LL | println!("10 / 4 is {}", 2.5);
| ^^^
error: literal with an empty format string
--> $DIR/print_literal.rs:27:28
|
LL | println!("2 + 1 = {}", 3);
| ^
error: literal with an empty format string error: literal with an empty format string
--> $DIR/print_literal.rs:32:25 --> $DIR/print_literal.rs:32:25
| |
@@ -84,5 +66,5 @@ error: literal with an empty format string
LL | println!("{bar} {foo}", foo = "hello", bar = "world"); LL | println!("{bar} {foo}", foo = "hello", bar = "world");
| ^^^^^^^ | ^^^^^^^
error: aborting due to 14 previous errors error: aborting due to 11 previous errors

View File

@@ -0,0 +1,11 @@
#![allow(unused)]
#![warn(clippy::redundant_slicing)]
fn main() {
let x: &[u32] = &[0];
let err = &x[..];
let v = vec![0];
let ok = &v[..];
let err = &(&v[..])[..];
}

View File

@@ -0,0 +1,16 @@
error: redundant slicing of the whole range
--> $DIR/redundant_slicing.rs:6:15
|
LL | let err = &x[..];
| ^^^^^^ help: use the original slice instead: `x`
|
= note: `-D clippy::redundant-slicing` implied by `-D warnings`
error: redundant slicing of the whole range
--> $DIR/redundant_slicing.rs:10:15
|
LL | let err = &(&v[..])[..];
| ^^^^^^^^^^^^^ help: use the original slice instead: `(&v[..])`
error: aborting due to 2 previous errors

View File

@@ -101,3 +101,8 @@ pub(crate) struct DirSizes {
pub(crate) numb_reg_cache_entries: u64, pub(crate) numb_reg_cache_entries: u64,
pub(crate) numb_reg_src_checkouts: u64, pub(crate) numb_reg_src_checkouts: u64,
} }
fn ignore_underscore_prefix() {
let hello: ();
let _hello: ();
}

View File

@@ -81,6 +81,62 @@ fn single_match_know_enum() {
} }
} }
// issue #173
fn if_suggestion() {
let x = "test";
match x {
"test" => println!(),
_ => (),
}
#[derive(PartialEq, Eq)]
enum Foo {
A,
B,
C(u32),
}
let x = Foo::A;
match x {
Foo::A => println!(),
_ => (),
}
const FOO_C: Foo = Foo::C(0);
match x {
FOO_C => println!(),
_ => (),
}
match &&x {
Foo::A => println!(),
_ => (),
}
let x = &x;
match &x {
Foo::A => println!(),
_ => (),
}
enum Bar {
A,
B,
}
impl PartialEq for Bar {
fn eq(&self, rhs: &Self) -> bool {
matches!((self, rhs), (Self::A, Self::A) | (Self::B, Self::B))
}
}
impl Eq for Bar {}
let x = Bar::A;
match x {
Bar::A => println!(),
_ => (),
}
}
macro_rules! single_match { macro_rules! single_match {
($num:literal) => { ($num:literal) => {
match $num { match $num {

View File

@@ -1,4 +1,4 @@
error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match.rs:8:5 --> $DIR/single_match.rs:8:5
| |
LL | / match x { LL | / match x {
@@ -17,7 +17,7 @@ LL | println!("{:?}", y);
LL | }; LL | };
| |
error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match.rs:16:5 --> $DIR/single_match.rs:16:5
| |
LL | / match x { LL | / match x {
@@ -29,7 +29,7 @@ LL | | _ => (),
LL | | } LL | | }
| |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }` | |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }`
error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match.rs:25:5 --> $DIR/single_match.rs:25:5
| |
LL | / match z { LL | / match z {
@@ -38,7 +38,7 @@ LL | | _ => {},
LL | | }; LL | | };
| |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }` | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }`
error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match.rs:54:5 --> $DIR/single_match.rs:54:5
| |
LL | / match x { LL | / match x {
@@ -47,7 +47,7 @@ LL | | None => (),
LL | | }; LL | | };
| |_____^ help: try this: `if let Some(y) = x { dummy() }` | |_____^ help: try this: `if let Some(y) = x { dummy() }`
error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match.rs:59:5 --> $DIR/single_match.rs:59:5
| |
LL | / match y { LL | / match y {
@@ -56,7 +56,7 @@ LL | | Err(..) => (),
LL | | }; LL | | };
| |_____^ help: try this: `if let Ok(y) = y { dummy() }` | |_____^ help: try this: `if let Ok(y) = y { dummy() }`
error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match.rs:66:5 --> $DIR/single_match.rs:66:5
| |
LL | / match c { LL | / match c {
@@ -65,5 +65,59 @@ LL | | Cow::Owned(..) => (),
LL | | }; LL | | };
| |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }` | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }`
error: aborting due to 6 previous errors error: you seem to be trying to use `match` for an equality check. Consider using `if`
--> $DIR/single_match.rs:87:5
|
LL | / match x {
LL | | "test" => println!(),
LL | | _ => (),
LL | | }
| |_____^ help: try this: `if x == "test" { println!() }`
error: you seem to be trying to use `match` for an equality check. Consider using `if`
--> $DIR/single_match.rs:100:5
|
LL | / match x {
LL | | Foo::A => println!(),
LL | | _ => (),
LL | | }
| |_____^ help: try this: `if x == Foo::A { println!() }`
error: you seem to be trying to use `match` for an equality check. Consider using `if`
--> $DIR/single_match.rs:106:5
|
LL | / match x {
LL | | FOO_C => println!(),
LL | | _ => (),
LL | | }
| |_____^ help: try this: `if x == FOO_C { println!() }`
error: you seem to be trying to use `match` for an equality check. Consider using `if`
--> $DIR/single_match.rs:111:5
|
LL | / match &&x {
LL | | Foo::A => println!(),
LL | | _ => (),
LL | | }
| |_____^ help: try this: `if x == Foo::A { println!() }`
error: you seem to be trying to use `match` for an equality check. Consider using `if`
--> $DIR/single_match.rs:117:5
|
LL | / match &x {
LL | | Foo::A => println!(),
LL | | _ => (),
LL | | }
| |_____^ help: try this: `if x == &Foo::A { println!() }`
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match.rs:134:5
|
LL | / match x {
LL | | Bar::A => println!(),
LL | | _ => (),
LL | | }
| |_____^ help: try this: `if let Bar::A = x { println!() }`
error: aborting due to 12 previous errors

View File

@@ -1,4 +1,4 @@
error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match_else.rs:14:5 --> $DIR/single_match_else.rs:14:5
| |
LL | / match ExprNode::Butterflies { LL | / match ExprNode::Butterflies {
@@ -19,7 +19,7 @@ LL | None
LL | } LL | }
| |
error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match_else.rs:70:5 --> $DIR/single_match_else.rs:70:5
| |
LL | / match Some(1) { LL | / match Some(1) {
@@ -39,7 +39,7 @@ LL | return
LL | } LL | }
| |
error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match_else.rs:79:5 --> $DIR/single_match_else.rs:79:5
| |
LL | / match Some(1) { LL | / match Some(1) {

View File

@@ -0,0 +1,37 @@
#![warn(clippy::size_of_in_element_count)]
#![allow(clippy::ptr_offset_with_cast)]
use std::mem::{size_of, size_of_val};
use std::ptr::{copy, copy_nonoverlapping, write_bytes};
fn main() {
const SIZE: usize = 128;
const HALF_SIZE: usize = SIZE / 2;
const DOUBLE_SIZE: usize = SIZE * 2;
let mut x = [2u8; SIZE];
let mut y = [2u8; SIZE];
// Count expression involving multiplication of size_of (Should trigger the lint)
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
// Count expression involving nested multiplications of size_of (Should trigger the lint)
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) };
// Count expression involving divisions of size_of (Should trigger the lint)
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::<u8>() / 2) };
// Count expression involving divisions by size_of (Should not trigger the lint)
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / size_of::<u8>()) };
// Count expression involving divisions by multiple size_of (Should not trigger the lint)
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 * size_of::<u8>())) };
// Count expression involving recursive divisions by size_of (Should trigger the lint)
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::<u8>())) };
// No size_of calls (Should not trigger the lint)
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) };
// Different types for pointee and size_of (Should not trigger the lint)
unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::<u16>() / 2 * SIZE) };
}

View File

@@ -0,0 +1,35 @@
error: found a count of bytes instead of a count of elements of `T`
--> $DIR/expressions.rs:15:62
|
LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::size-of-in-element-count` implied by `-D warnings`
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> $DIR/expressions.rs:18:62
|
LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> $DIR/expressions.rs:21:47
|
LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::<u8>() / 2) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> $DIR/expressions.rs:30:47
|
LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::<u8>())) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: aborting due to 4 previous errors

View File

@@ -43,19 +43,4 @@ fn main() {
y.as_mut_ptr().wrapping_add(size_of::<u8>()); y.as_mut_ptr().wrapping_add(size_of::<u8>());
unsafe { y.as_ptr().offset(size_of::<u8>() as isize) }; unsafe { y.as_ptr().offset(size_of::<u8>() as isize) };
y.as_mut_ptr().wrapping_offset(size_of::<u8>() as isize); y.as_mut_ptr().wrapping_offset(size_of::<u8>() as isize);
// Count expression involving multiplication of size_of (Should trigger the lint)
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
// Count expression involving nested multiplications of size_of (Should trigger the lint)
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) };
// Count expression involving divisions of size_of (Should trigger the lint)
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::<u8>() / 2) };
// No size_of calls (Should not trigger the lint)
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) };
// Different types for pointee and size_of (Should not trigger the lint)
unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::<u16>() / 2 * SIZE) };
} }

View File

@@ -1,5 +1,5 @@
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:18:68 --> $DIR/functions.rs:18:68
| |
LL | unsafe { copy_nonoverlapping::<u8>(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) }; LL | unsafe { copy_nonoverlapping::<u8>(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | unsafe { copy_nonoverlapping::<u8>(x.as_ptr(), y.as_mut_ptr(), size_of:
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:19:62 --> $DIR/functions.rs:19:62
| |
LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) };
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:21:49 --> $DIR/functions.rs:21:49
| |
LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::<u8>()) }; LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::<u8>()) };
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::<u8>()) };
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:22:64 --> $DIR/functions.rs:22:64
| |
LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::<u8>()) }; LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::<u8>()) };
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@@ -32,7 +32,7 @@ LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::<u8
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:23:51 --> $DIR/functions.rs:23:51
| |
LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::<u8>()) }; LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::<u8>()) };
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::<u8>()) };
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:24:66 --> $DIR/functions.rs:24:66
| |
LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::<u8>()) }; LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::<u8>()) };
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::<
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:26:47 --> $DIR/functions.rs:26:47
| |
LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) }; LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:27:47 --> $DIR/functions.rs:27:47
| |
LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) };
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) };
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:29:46 --> $DIR/functions.rs:29:46
| |
LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::<u8>() * SIZE) }; LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::<u8>() * SIZE) };
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:30:47 --> $DIR/functions.rs:30:47
| |
LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::<u8>() * SIZE) }; LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
@@ -80,7 +80,7 @@ LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::<u8>() * SIZE) };
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:32:66 --> $DIR/functions.rs:32:66
| |
LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::<u8>() * SIZE) }; LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
@@ -88,7 +88,7 @@ LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::<
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:34:46 --> $DIR/functions.rs:34:46
| |
LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE); LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE);
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
@@ -96,7 +96,7 @@ LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE);
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:35:38 --> $DIR/functions.rs:35:38
| |
LL | slice_from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE); LL | slice_from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE);
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
@@ -104,7 +104,7 @@ LL | slice_from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE);
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:37:49 --> $DIR/functions.rs:37:49
| |
LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE) }; LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
@@ -112,7 +112,7 @@ LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE) };
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:38:41 --> $DIR/functions.rs:38:41
| |
LL | unsafe { from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE) }; LL | unsafe { from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
@@ -120,7 +120,7 @@ LL | unsafe { from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE) };
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:40:33 --> $DIR/functions.rs:40:33
| |
LL | unsafe { y.as_mut_ptr().sub(size_of::<u8>()) }; LL | unsafe { y.as_mut_ptr().sub(size_of::<u8>()) };
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@@ -128,7 +128,7 @@ LL | unsafe { y.as_mut_ptr().sub(size_of::<u8>()) };
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:41:29 --> $DIR/functions.rs:41:29
| |
LL | y.as_ptr().wrapping_sub(size_of::<u8>()); LL | y.as_ptr().wrapping_sub(size_of::<u8>());
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@@ -136,7 +136,7 @@ LL | y.as_ptr().wrapping_sub(size_of::<u8>());
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:42:29 --> $DIR/functions.rs:42:29
| |
LL | unsafe { y.as_ptr().add(size_of::<u8>()) }; LL | unsafe { y.as_ptr().add(size_of::<u8>()) };
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@@ -144,7 +144,7 @@ LL | unsafe { y.as_ptr().add(size_of::<u8>()) };
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:43:33 --> $DIR/functions.rs:43:33
| |
LL | y.as_mut_ptr().wrapping_add(size_of::<u8>()); LL | y.as_mut_ptr().wrapping_add(size_of::<u8>());
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@@ -152,7 +152,7 @@ LL | y.as_mut_ptr().wrapping_add(size_of::<u8>());
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:44:32 --> $DIR/functions.rs:44:32
| |
LL | unsafe { y.as_ptr().offset(size_of::<u8>() as isize) }; LL | unsafe { y.as_ptr().offset(size_of::<u8>() as isize) };
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -160,36 +160,12 @@ LL | unsafe { y.as_ptr().offset(size_of::<u8>() as isize) };
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:45:36 --> $DIR/functions.rs:45:36
| |
LL | y.as_mut_ptr().wrapping_offset(size_of::<u8>() as isize); LL | y.as_mut_ptr().wrapping_offset(size_of::<u8>() as isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T` error: aborting due to 21 previous errors
--> $DIR/size_of_in_element_count.rs:48:62
|
LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:51:62
|
LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> $DIR/size_of_in_element_count.rs:54:47
|
LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::<u8>() / 2) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: aborting due to 24 previous errors

View File

@@ -1,46 +1,59 @@
error: used sort instead of sort_unstable to sort primitive type `i32` error: used `sort` on primitive type `i32`
--> $DIR/stable_sort_primitive.rs:7:5 --> $DIR/stable_sort_primitive.rs:7:5
| |
LL | vec.sort(); LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()` | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
| |
= note: `-D clippy::stable-sort-primitive` implied by `-D warnings` = note: `-D clippy::stable-sort-primitive` implied by `-D warnings`
= note: an unstable sort would perform faster without any observable difference for this data type
error: used sort instead of sort_unstable to sort primitive type `bool` error: used `sort` on primitive type `bool`
--> $DIR/stable_sort_primitive.rs:9:5 --> $DIR/stable_sort_primitive.rs:9:5
| |
LL | vec.sort(); LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()` | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
= note: an unstable sort would perform faster without any observable difference for this data type
error: used sort instead of sort_unstable to sort primitive type `char` error: used `sort` on primitive type `char`
--> $DIR/stable_sort_primitive.rs:11:5 --> $DIR/stable_sort_primitive.rs:11:5
| |
LL | vec.sort(); LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()` | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
= note: an unstable sort would perform faster without any observable difference for this data type
error: used sort instead of sort_unstable to sort primitive type `str` error: used `sort` on primitive type `str`
--> $DIR/stable_sort_primitive.rs:13:5 --> $DIR/stable_sort_primitive.rs:13:5
| |
LL | vec.sort(); LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()` | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
= note: an unstable sort would perform faster without any observable difference for this data type
error: used sort instead of sort_unstable to sort primitive type `tuple` error: used `sort` on primitive type `tuple`
--> $DIR/stable_sort_primitive.rs:15:5 --> $DIR/stable_sort_primitive.rs:15:5
| |
LL | vec.sort(); LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()` | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
= note: an unstable sort would perform faster without any observable difference for this data type
error: used sort instead of sort_unstable to sort primitive type `array` error: used `sort` on primitive type `array`
--> $DIR/stable_sort_primitive.rs:17:5 --> $DIR/stable_sort_primitive.rs:17:5
| |
LL | vec.sort(); LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()` | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
= note: an unstable sort would perform faster without any observable difference for this data type
error: used sort instead of sort_unstable to sort primitive type `i32` error: used `sort` on primitive type `i32`
--> $DIR/stable_sort_primitive.rs:19:5 --> $DIR/stable_sort_primitive.rs:19:5
| |
LL | arr.sort(); LL | arr.sort();
| ^^^^^^^^^^ help: try: `arr.sort_unstable()` | ^^^^^^^^^^ help: try: `arr.sort_unstable()`
|
= note: an unstable sort would perform faster without any observable difference for this data type
error: aborting due to 7 previous errors error: aborting due to 7 previous errors

View File

@@ -27,7 +27,7 @@ fn buggy_ab_cmp(s1: &S, s2: &S) -> bool {
s1.a < s2.a && s1.a < s2.b s1.a < s2.a && s1.a < s2.b
} }
struct SAOnly { struct SaOnly {
a: i32, a: i32,
} }
@@ -37,13 +37,13 @@ impl S {
} }
} }
fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SAOnly) -> bool { fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SaOnly) -> bool {
// This is superficially similar to `buggy_ab_cmp`, but we should not suggest // This is superficially similar to `buggy_ab_cmp`, but we should not suggest
// `s2.b` since that is invalid. // `s2.b` since that is invalid.
s1.a < s2.a && s1.a() < s1.b s1.a < s2.a && s1.a() < s1.b
} }
fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SAOnly) -> bool { fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SaOnly) -> bool {
macro_rules! s1 { macro_rules! s1 {
() => { () => {
S { S {
@@ -60,7 +60,7 @@ fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SAOnly) -> bool
s1.a < s2.a && s1!().a < s1.b s1.a < s2.a && s1!().a < s1.b
} }
fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SAOnly) -> bool { fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SaOnly) -> bool {
// There's two `s1.b`, but we should not suggest `s2.b` since that is invalid // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid
s1.a < s2.a && s1.b < s1.b s1.a < s2.a && s1.b < s1.b
} }

Some files were not shown because too many files have changed in this diff Show More