Auto merge of #81560 - flip1995:clippyup, r=Manishearth

Update Clippy

r? `@Manishearth`

Biweekly Clippy update (2 days late)
This commit is contained in:
bors 2021-01-30 23:55:16 +00:00
commit b3897e3d13
114 changed files with 6061 additions and 454 deletions

View File

@ -1,6 +1,7 @@
[alias]
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-lintcheck = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --features lintcheck -- lintcheck"
[build]
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
[`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
[`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_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
@ -1937,6 +1938,8 @@ Released 2018-09-13
[`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
[`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
[`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
@ -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_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
[`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
[`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
@ -2033,6 +2037,8 @@ Released 2018-09-13
[`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
[`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_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
@ -2161,6 +2167,7 @@ Released 2018-09-13
[`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_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
[`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
@ -2271,6 +2278,7 @@ Released 2018-09-13
[`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_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_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

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
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
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
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
[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash
[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_instructions]: https://bors.rust-lang.org/
[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>"]
edition = "2018"
[dependencies]
bytecount = "0.6"
clap = "2.33"
flate2 = { version = "1.0.19", optional = true }
itertools = "0.9"
opener = "0.4"
regex = "1"
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0", optional = true }
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"
[features]
lintcheck = ["flate2", "serde_json", "tar", "toml", "ureq", "serde"]
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 fmt;
pub mod lintcheck;
pub mod new_lint;
pub mod ra_setup;
pub mod serve;

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 clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};
#[cfg(feature = "lintcheck")]
use clippy_dev::lintcheck;
fn main() {
let matches = get_clap_config();
@ -10,6 +13,10 @@ fn main() {
("bless", Some(matches)) => {
bless::bless(matches.is_present("ignore-timestamp"));
},
#[cfg(feature = "lintcheck")]
("lintcheck", Some(matches)) => {
lintcheck::run(&matches);
},
("fmt", Some(matches)) => {
fmt::run(matches.is_present("check"), matches.is_present("verbose"));
},
@ -46,7 +53,18 @@ fn main() {
}
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::with_name("bless")
.about("bless the test output changes")
@ -163,6 +181,10 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
.validator_os(serve::validate_port),
)
.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::File;
use std::io::prelude::*;

View File

@ -8,6 +8,14 @@ use crate::utils::span_lint_and_help;
declare_clippy_lint! {
/// **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
/// conversions, including silently lossy conversions and dangerous coercions.
/// 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,
/// 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:**
///
@ -57,7 +57,7 @@ declare_clippy_lint! {
/// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
/// 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:**
///

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 mut helper = CCHelper { cc: 1, returns: 0 };
let mut helper = CcHelper { cc: 1, returns: 0 };
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_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::result_type) {
returns
@ -136,12 +136,12 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity {
}
}
struct CCHelper {
struct CcHelper {
cc: u64,
returns: u64,
}
impl<'tcx> Visitor<'tcx> for CCHelper {
impl<'tcx> Visitor<'tcx> for CcHelper {
type Map = Map<'tcx>;
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 if_chain::if_chain;
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_middle::ty::{DefIdTree, TyCtxt};
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());
// match expression must be a local binding
// match <local> { .. }
if let ExprKind::Path(QPath::Resolved(None, path)) = expr_in.kind;
if let Res::Local(binding_id) = path.res;
if let Some(binding_id) = addr_adjusted_binding(expr_in, cx);
// 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));
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
if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body);
// 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
if !arms_inner.iter().any(|arm| LocalUsedVisitor::new(binding_id).check_arm(arm));
then {
@ -170,3 +174,20 @@ fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool {
}
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,
"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 rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, IntTy, UintTy};
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, IntTy, UintTy};
use rustc_session::{declare_lint_pass, declare_tool_lint};
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| {
cx.tcx.infer_ctxt().enter(|infcx| {
for FulfillmentError { obligation, .. } in send_errors {
infcx.maybe_note_obligation_cause_for_async_await(
db,
&obligation,
);
if let Trait(trait_pred, _) =
obligation.predicate.kind().skip_binder()
{
infcx.maybe_note_obligation_cause_for_async_await(db, &obligation);
if let Trait(trait_pred, _) = obligation.predicate.kind().skip_binder() {
db.note(&format!(
"`{}` doesn't implement `{}`",
trait_pred.self_ty(),

View File

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

View File

@ -170,6 +170,7 @@ mod blocks_in_if_conditions;
mod booleans;
mod bytecount;
mod cargo_common_metadata;
mod case_sensitive_file_extension_comparisons;
mod checked_conversions;
mod cognitive_complexity;
mod collapsible_if;
@ -199,6 +200,7 @@ mod escape;
mod eta_reduction;
mod eval_order_dependence;
mod excessive_bools;
mod exhaustive_items;
mod exit;
mod explicit_write;
mod fallible_impl_from;
@ -300,6 +302,7 @@ mod redundant_closure_call;
mod redundant_else;
mod redundant_field_names;
mod redundant_pub_crate;
mod redundant_slicing;
mod redundant_static_lifetimes;
mod ref_option_ref;
mod reference;
@ -339,6 +342,7 @@ mod unused_self;
mod unused_unit;
mod unwrap;
mod unwrap_in_result;
mod upper_case_acronyms;
mod use_self;
mod useless_conversion;
mod vec;
@ -506,6 +510,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
"clippy::unknown_clippy_lints",
"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`
// 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,
&bytecount::NAIVE_BYTECOUNT,
&cargo_common_metadata::CARGO_COMMON_METADATA,
&case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
&checked_conversions::CHECKED_CONVERSIONS,
&cognitive_complexity::COGNITIVE_COMPLEXITY,
&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,
&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
&excessive_bools::STRUCT_EXCESSIVE_BOOLS,
&exhaustive_items::EXHAUSTIVE_ENUMS,
&exhaustive_items::EXHAUSTIVE_STRUCTS,
&exit::EXIT,
&explicit_write::EXPLICIT_WRITE,
&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_NEXT,
&methods::FILTER_NEXT,
&methods::FIND_MAP,
&methods::FLAT_MAP_IDENTITY,
&methods::FROM_ITER_INSTEAD_OF_COLLECT,
&methods::GET_UNWRAP,
&methods::INEFFICIENT_TO_STRING,
&methods::INSPECT_FOR_EACH,
&methods::INTO_ITER_ON_REF,
&methods::ITERATOR_STEP_BY_ZERO,
&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_ZERO,
&methods::ITER_SKIP_NEXT,
&methods::MANUAL_FILTER_MAP,
&methods::MANUAL_FIND_MAP,
&methods::MANUAL_SATURATING_ARITHMETIC,
&methods::MAP_COLLECT_RESULT_UNIT,
&methods::MAP_FLATTEN,
@ -850,6 +863,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&redundant_else::REDUNDANT_ELSE,
&redundant_field_names::REDUNDANT_FIELD_NAMES,
&redundant_pub_crate::REDUNDANT_PUB_CRATE,
&redundant_slicing::REDUNDANT_SLICING,
&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
&ref_option_ref::REF_OPTION_REF,
&reference::DEREF_ADDROF,
@ -942,6 +956,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&unwrap::PANICKING_UNWRAP,
&unwrap::UNNECESSARY_UNWRAP,
&unwrap_in_result::UNWRAP_IN_RESULT,
&upper_case_acronyms::UPPER_CASE_ACRONYMS,
&use_self::USE_SELF,
&useless_conversion::USELESS_CONVERSION,
&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 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;
store.register_late_pass(move || box types::Types::new(vec_box_size_threshold));
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 missing_doc::MissingDoc::new());
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 partialeq_ne_impl::PartialEqNeImpl);
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;
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 upper_case_acronyms::UpperCaseAcronyms);
store.register_late_pass(|| box default::Default::default());
store.register_late_pass(|| box unused_self::UnusedSelf);
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 vec_init_then_push::VecInitThenPush::default());
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![
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(&dbg_macro::DBG_MACRO),
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(&float_literal::LOSSY_FLOAT_LITERAL),
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_REFCELL_REF),
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(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
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(&methods::FILTER_MAP),
LintId::of(&methods::FILTER_MAP_NEXT),
LintId::of(&methods::FIND_MAP),
LintId::of(&methods::INEFFICIENT_TO_STRING),
LintId::of(&methods::MAP_FLATTEN),
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::FLAT_MAP_IDENTITY),
LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(&methods::INSPECT_FOR_EACH),
LintId::of(&methods::INTO_ITER_ON_REF),
LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
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_ZERO),
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::MAP_COLLECT_RESULT_UNIT),
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_closure_call::REDUNDANT_CLOSURE_CALL),
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
LintId::of(&redundant_slicing::REDUNDANT_SLICING),
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(&reference::DEREF_ADDROF),
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(&unwrap::PANICKING_UNWRAP),
LintId::of(&unwrap::UNNECESSARY_UNWRAP),
LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS),
LintId::of(&useless_conversion::USELESS_CONVERSION),
LintId::of(&vec::USELESS_VEC),
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(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(&unused_unit::UNUSED_UNIT),
LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS),
LintId::of(&write::PRINTLN_EMPTY_STRING),
LintId::of(&write::PRINT_LITERAL),
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::FILTER_NEXT),
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::SEARCH_IS_SOME),
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(&ranges::RANGE_ZIP_WITH_LEN),
LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL),
LintId::of(&redundant_slicing::REDUNDANT_SLICING),
LintId::of(&reference::DEREF_ADDROF),
LintId::of(&reference::REF_IN_DEREF),
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 {
// As the `filter` and `map` below do different things, I think putting together
// just increases complexity. (cc #3188 and #4193)
#[allow(clippy::filter_map)]
stmts
.iter()
.filter_map(move |stmt| match stmt.kind {

View File

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

View File

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

View File

@ -131,7 +131,7 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) ->
Some(expr.span)
},
hir::ExprKind::Block(ref block, _) => {
match (&block.stmts[..], block.expr.as_ref()) {
match (block.stmts, block.expr.as_ref()) {
(&[], Some(inner_expr)) => {
// If block only contains an expression,
// 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::usage::is_unused;
use crate::utils::{
expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks,
snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note,
span_lint_and_sugg, span_lint_and_then,
expr_block, get_arg_name, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of,
is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg,
peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt,
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 if_chain::if_chain;
@ -728,20 +728,60 @@ fn report_single_match_single_pattern(
let els_str = els.map_or(String::new(), |els| {
format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
});
let (msg, sugg) = if_chain! {
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait();
if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]);
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 {} = {} {}{}",
snippet(cx, arms[0].pat.span, ".."),
snippet(cx, ex.span, ".."),
expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
els_str,
);
(msg, sugg)
}
};
span_lint_and_sugg(
cx,
lint,
expr.span,
"you seem to be trying to use match for destructuring a single pattern. Consider using `if \
let`",
msg,
"try this",
format!(
"if let {} = {} {}{}",
snippet(cx, arms[0].pat.span, ".."),
snippet(cx, ex.span, ".."),
expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
els_str,
),
sugg,
Applicability::HasPlaceholders,
);
}
@ -1185,6 +1225,14 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
} else {
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(
cx,
MATCH_LIKE_MATCHES_MACRO,
@ -1194,7 +1242,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
format!(
"{}matches!({}, {})",
if b0 { "" } else { "!" },
snippet_with_applicability(cx, ex.span, "..", &mut applicability),
snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
pat_and_guard,
),
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 inefficient_to_string;
mod inspect_for_each;
mod manual_saturating_arithmetic;
mod option_map_unwrap_or;
mod unnecessary_filter_map;
@ -14,7 +15,8 @@ use if_chain::if_chain;
use rustc_ast::ast;
use rustc_errors::Applicability;
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_middle::lint::in_external_macro;
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"
}
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! {
/// **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"
}
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! {
/// **What it does:** Checks for an iterator or string search (such as `find()`,
/// `position()`, or `rposition()`) followed by a call to `is_some()`.
@ -1405,6 +1437,36 @@ declare_clippy_lint! {
"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 {
msrv: Option<RustcVersion>,
}
@ -1442,9 +1504,10 @@ impl_lint_pass!(Methods => [
FILTER_NEXT,
SKIP_WHILE_NEXT,
FILTER_MAP,
MANUAL_FILTER_MAP,
MANUAL_FIND_MAP,
FILTER_MAP_NEXT,
FLAT_MAP_IDENTITY,
FIND_MAP,
MAP_FLATTEN,
ITERATOR_STEP_BY_ZERO,
ITER_NEXT_SLICE,
@ -1467,6 +1530,7 @@ impl_lint_pass!(Methods => [
UNNECESSARY_LAZY_EVALUATIONS,
MAP_COLLECT_RESULT_UNIT,
FROM_ITER_INSTEAD_OF_COLLECT,
INSPECT_FOR_EACH,
]);
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", "skip_while"] => lint_skip_while_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]),
["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_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]),
@ -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"),
["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]),
["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`
fn lint_filter_map<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
_filter_args: &'tcx [hir::Expr<'_>],
_map_args: &'tcx [hir::Expr<'_>],
) {
// lint if caller of `.filter().map()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
let msg = "called `filter(..).map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead";
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
/// lint use of `filter().map()` or `find().map()` for `Iterators`
fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) {
if_chain! {
if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind;
if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind;
if match_trait_method(cx, map_recv, &paths::ITERATOR);
// filter(|x| ...is_some())...
if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind;
let filter_body = cx.tcx.hir().body(filter_body_id);
if let [filter_param] = filter_body.params;
// 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`
fn lint_filter_map_map<'tcx>(
cx: &LateContext<'tcx>,
@ -3026,7 +3137,7 @@ fn lint_filter_map_map<'tcx>(
_filter_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) {
let msg = "called `filter_map(..).map(..)` on an `Iterator`";
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| {
// Note that we do not want to deal with qualified predicates here.
match obligation.predicate.kind().no_bound_vars() {
Some(ty::PredicateKind::Trait(pred, _)) if pred.def_id() != sized_trait => {
Some(pred)
},
Some(ty::PredicateKind::Trait(pred, _)) if pred.def_id() != sized_trait => Some(pred),
_ => None,
}
})

View File

@ -199,6 +199,10 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
);
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();
if count < 3 {
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) {
if_chain! {
if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind;
if let TyKind::Path(ref path) = ty.kind;
if let QPath::Resolved(None, ref pp) = *path;
if let TyKind::Path(QPath::Resolved(None, ref pp)) = ty.kind;
if let [ref bx] = *pp.segments;
if let Some(ref params) = bx.args;
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 if_chain::if_chain;
use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
@ -110,6 +111,12 @@ declare_clippy_lint! {
/// let point = Point(30, 20);
/// let x = (&point).0;
/// ```
/// Use instead:
/// ```rust
/// # struct Point(u32, u32);
/// # let point = Point(30, 20);
/// let x = point.0;
/// ```
pub REF_IN_DEREF,
complexity,
"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::AddrOf(_, _, ref inner) = parened.kind;
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(
cx,
REF_IN_DEREF,
object.span,
"creating a reference that is immediately dereferenced",
"try this",
snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(),
sugg.to_string(),
applicability,
);
}

View File

@ -131,7 +131,16 @@ impl<'tcx> LateLintPass<'tcx> for Return {
_: HirId,
) {
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(..) => {
if let ExprKind::Block(ref block, _) = body.value.kind {
check_block_return(cx, block);

View File

@ -18,9 +18,9 @@ declare_clippy_lint! {
"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<'_>) {
if let ItemKind::Impl(Impl {
of_trait: Some(ref trait_ref),

View File

@ -35,10 +35,11 @@ declare_clippy_lint! {
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 {
ExprKind::Call(count_func, _func_args) => {
if_chain! {
if !inverted;
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 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 => {
get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right))
ExprKind::Binary(op, left, right) if BinOpKind::Mul == 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),
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,
}
}
@ -128,7 +132,7 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount {
// Find a size_of call in the count parameter expression and
// 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);
then {
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;
@ -107,25 +107,32 @@ fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option
impl LateLintPass<'_> for StableSortPrimitive {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if let Some(detection) = detect_stable_sort_primitive(cx, expr) {
span_lint_and_sugg(
span_lint_and_then(
cx,
STABLE_SORT_PRIMITIVE,
expr.span,
format!(
"used {} instead of {} to sort primitive type `{}`",
"used `{}` on primitive type `{}`",
detection.method.stable_name(),
detection.method.unstable_name(),
detection.slice_type,
)
.as_str(),
"try",
format!(
"{}.{}({})",
detection.slice_name,
detection.method.unstable_name(),
detection.method_args
),
Applicability::MachineApplicable,
|diag| {
diag.span_suggestion(
expr.span,
"try",
format!(
"{}.{}({})",
detection.slice_name,
detection.method.unstable_name(),
detection.method_args,
),
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",
"OCaml",
"OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap",
"WebGL",
"TensorFlow",
"TrueType",
"iOS", "macOS",

View File

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

View File

@ -158,8 +158,7 @@ pub fn for_loop<'tcx>(
/// `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>)> {
if_chain! {
if let hir::ExprKind::Loop(block, _, hir::LoopSource::While, _) = &expr.kind;
if let hir::Block { expr: Some(expr), .. } = &**block;
if let hir::ExprKind::Loop(hir::Block { expr: Some(expr), .. }, _, hir::LoopSource::While, _) = &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::Arm { body, .. }, ..] = &arms[..];

View File

@ -24,6 +24,7 @@ pub struct SpanlessEq<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
allow_side_effects: bool,
expr_fallback: Option<Box<dyn Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
}
impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
@ -32,6 +33,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
cx,
maybe_typeck_results: cx.maybe_typeck_results(),
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.
pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
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)) => {
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::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re),
_ => 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 {

View File

@ -31,7 +31,6 @@ pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::hash::BuildHasherDefault;
use std::mem;
use if_chain::if_chain;
use rustc_ast::ast::{self, Attribute, LitKind};
@ -39,7 +38,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir as hir;
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::Node;
use rustc_hir::{
@ -48,6 +47,7 @@ use rustc_hir::{
};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::exports::Export;
use rustc_middle::hir::map::Map;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
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.
pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> {
let crates = cx.tcx.crates();
let krate = crates
.iter()
.find(|&&krate| cx.tcx.crate_name(krate).as_str() == path[0]);
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 segment = match path_it.next() {
Some(segment) => segment,
None => return None,
};
// `get_def_path` seems to generate these empty segments for extern blocks.
// We can just ignore them.
if segment.is_empty() {
continue;
}
let result = SmallVec::<[_; 8]>::new();
for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() {
if item.ident.name.as_str() == *segment {
if path_it.peek().is_none() {
return Some(item.res);
}
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 {
None
#[allow(clippy::shadow_unrelated)] // false positive #6563
pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<Res> {
fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export<HirId>> {
tcx.item_children(def_id)
.iter()
.find(|item| item.ident.name.as_str() == name)
}
let (krate, first, path) = match *path {
[krate, first, ref path @ ..] => (krate, first, path),
_ => 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.
// We can just ignore them.
.filter(|segment| !segment.is_empty())
// for each segment, find the child item
.try_fold(first, |item, segment| {
let def_id = item.res.def_id();
if let Some(item) = item_child_by_name(tcx, def_id, segment) {
Some(item)
} else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
// it is not a child item so check inherent impl items
tcx.inherent_impls(def_id)
.iter()
.find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
} else {
None
}
})?;
Some(last.res)
}
/// 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 {
if_chain! {
if let TyKind::Path(ref qp) = slf.kind;
if let QPath::Resolved(None, ref path) = *qp;
if let TyKind::Path(QPath::Resolved(None, ref path)) = slf.kind;
if let Res::SelfTy(..) = path.res;
then {
return true
@ -1655,6 +1632,44 @@ where
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_rules! unwrap_cargo_metadata {
($cx: ident, $lint: ident, $deps: expr) => {{

View File

@ -2,7 +2,8 @@ use std::borrow::Cow;
use std::ops::Range;
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::tokenstream::TokenStream;
use rustc_errors::Applicability;
@ -437,7 +438,7 @@ impl Write {
return (Some(fmtstr), None);
};
match &token_expr.kind {
ExprKind::Lit(_) => {
ExprKind::Lit(lit) if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => {
let mut all_simple = true;
let mut seen = false;
for arg in &args {
@ -457,8 +458,11 @@ impl Write {
idx += 1;
},
ExprKind::Assign(lhs, rhs, _) => {
if let ExprKind::Lit(_) = rhs.kind {
if let ExprKind::Path(_, p) = &lhs.kind {
if_chain! {
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 seen = false;
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_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! {
/// **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 let Adt(_, ref substs) = ty.kind();
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 layout.is_zst();
then {

View File

@ -23,6 +23,7 @@ because that's clearly a non-descriptive name.
- [Running rustfmt](#running-rustfmt)
- [Debugging](#debugging)
- [PR Checklist](#pr-checklist)
- [Adding configuration to a lint](#adding-configuration-to-a-lint)
- [Cheatsheet](#cheatsheet)
## Setup
@ -526,6 +527,81 @@ Before submitting your PR make sure you followed all of the basic requirements:
- \[ ] Added lint documentation
- \[ ] 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
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)
- [Building and Testing](#building-and-testing)
- [`cargo dev`](#cargo-dev)
- [Common Abbreviations](#common-abbreviations)
- [PR](#pr)
## Get the Code
@ -94,3 +95,22 @@ cargo dev ra_setup
We follow a rustc no merge-commit policy.
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

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2021-01-15"
channel = "nightly-2021-01-30"
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) {
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()) {
panic!("Found multiple rlibs for crate `{}`: `{:?}` and `{:?}", dep, old, path);
}

View File

@ -18,6 +18,9 @@ mod paths {
// Path with bad module
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() {}

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.
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 {

View File

@ -57,5 +57,41 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n));
| Replace this binding
= 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)>>>,
}
struct TS(Vec<Vec<Box<(u32, u32, u32, u32)>>>);
struct Ts(Vec<Vec<Box<(u32, u32, u32, u32)>>>);
enum E {
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
--> $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

View File

@ -1,5 +1,6 @@
// 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()
#![allow(clippy::upper_case_acronyms)]
trait TT {}

View File

@ -1,13 +1,13 @@
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
| ^^^^ lifetime mismatch
|
= note: expected reference `&(dyn TT + 'static)`
found reference `&dyn TT`
note: the anonymous lifetime #1 defined on the body at 11:13...
--> $DIR/ice-6256.rs:11:13
note: the anonymous lifetime #1 defined on the body at 12:13...
--> $DIR/ice-6256.rs:12:13
|
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::panic_params)]
#[warn(clippy::unknown_clippy_lints)]
#[warn(clippy::find_map)]
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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
--> $DIR/deprecated.rs:1:8
|
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
/// ECMAScript
/// OAuth GraphQL
/// WebGL
/// TeX LaTeX BibTeX BibLaTeX
/// CamelCase (see also #2395)
/// 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
--> $DIR/doc.rs:58:5
--> $DIR/doc.rs:59:5
|
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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.
| ^^^^^^^^^^^^^^^^^^^^^
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].
| ^^^^^^^^^^^^
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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `CamelCaseThing` between ticks in the documentation
--> $DIR/doc.rs:83:8
--> $DIR/doc.rs:84:8
|
LL | /// ## CamelCaseThing
| ^^^^^^^^^^^^^^
error: you should put `CamelCaseThing` between ticks in the documentation
--> $DIR/doc.rs:86:7
--> $DIR/doc.rs:87:7
|
LL | /// # CamelCaseThing
| ^^^^^^^^^^^^^^
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
| ^^^^^^^^^^^^^^
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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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
| ^^^^^^
error: you should put `BarQuz` between ticks in the documentation
--> $DIR/doc.rs:125:5
--> $DIR/doc.rs:126:5
|
LL | And BarQuz too.
| ^^^^^^
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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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
| ^^^^^^
error: you should put `BarQuz` between ticks in the documentation
--> $DIR/doc.rs:136:5
--> $DIR/doc.rs:137:5
|
LL | And BarQuz too.
| ^^^^^^
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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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
| ^^^^^^^^^^^^^^^^^^^^^^
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
| ^^^^^^^^^^^^^^^^^^^^^^^
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/
| ^^^^^^^^^^^^^^^^^^^^^^
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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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.
| ^^^^^^^^^^^^^^^^^^^

View File

@ -1,6 +1,6 @@
#![feature(non_ascii_idents)]
#![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 {
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`
--> $DIR/filter_methods.rs:8:21
|
@ -17,6 +8,7 @@ LL | | .filter(|&x| x == 0)
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()`
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
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,
};
}
{
// 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,
};
}
{
// 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 | | };
| |_________^ 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
#![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)]
struct TO {

View File

@ -1,7 +1,13 @@
// run-rustfix
#![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)]
struct TO {

View File

@ -1,5 +1,5 @@
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?);
| ^^^^^^^^^^^^^^^ help: try: `to.magic`
@ -7,79 +7,79 @@ LL | return Some(to.magic?);
= note: `-D clippy::needless-question-mark` implied by `-D warnings`
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?)
| ^^^^^^^^^^^^^^^ help: try: `to.magic`
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?)
| ^^^^^^^^^^^^^^^ help: try: `to.magic`
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?))
| ^^^^^^^^^^^^^^ help: try: `t.magic`
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?)
| ^^^^^^^^^^^^^^ help: try: `t.magic`
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?);
| ^^^^^^^^^^^^^ help: try: `tr.magic`
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?)
| ^^^^^^^^^^^^^ help: try: `tr.magic`
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?)
| ^^^^^^^^^^^^^ help: try: `tr.magic`
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?))
| ^^^^^^^^^^^^ help: try: `t.magic`
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?)
| ^^^^^^^^^^^^ help: try: `t.magic`
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?);
| ^^^^^^^^^^^^ help: try: `t.magic`
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
| ^^^^^^^^^^^^^ help: try: `to.magic`
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
| ^^^^^^^^^^^^^^^ help: try: `to.magic`
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
| ^^^^^^^^^^^^^ help: try: `to.magic`

View File

@ -101,6 +101,25 @@ fn test_return_in_macro() {
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() {
let _ = test_end_of_fn();
let _ = test_no_semicolon();

View File

@ -101,6 +101,25 @@ fn test_return_in_macro() {
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() {
let _ = test_end_of_fn();
let _ = test_no_semicolon();

View File

@ -84,5 +84,29 @@ error: unneeded `return` statement
LL | 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!("{number:>width$}", 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
println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
print!("Hello {}", "world");
println!("Hello {} {}", world, "world");
println!("Hello {}", "world");
println!("10 / 4 is {}", 2.5);
println!("2 + 1 = {}", 3);
// positional args don't change the fact
// that we're using a literal -- this should

View File

@ -1,41 +1,23 @@
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`
error: literal with an empty format string
--> $DIR/print_literal.rs:23:24
|
LL | print!("Hello {}", "world");
| ^^^^^^^
error: literal with an empty format string
--> $DIR/print_literal.rs:24:36
--> $DIR/print_literal.rs:26:36
|
LL | println!("Hello {} {}", world, "world");
| ^^^^^^^
error: literal with an empty format string
--> $DIR/print_literal.rs:25:26
--> $DIR/print_literal.rs:27:26
|
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
--> $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");
| ^^^^^^^
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_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 {
($num:literal) => {
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
|
LL | / match x {
@ -17,7 +17,7 @@ LL | println!("{:?}", y);
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
|
LL | / match x {
@ -29,7 +29,7 @@ LL | | _ => (),
LL | | }
| |_____^ 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
|
LL | / match z {
@ -38,7 +38,7 @@ LL | | _ => {},
LL | | };
| |_____^ 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
|
LL | / match x {
@ -47,7 +47,7 @@ LL | | None => (),
LL | | };
| |_____^ 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
|
LL | / match y {
@ -56,7 +56,7 @@ LL | | Err(..) => (),
LL | | };
| |_____^ 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
|
LL | / match c {
@ -65,5 +65,59 @@ LL | | Cow::Owned(..) => (),
LL | | };
| |_____^ 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
|
LL | / match ExprNode::Butterflies {
@ -19,7 +19,7 @@ LL | None
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
|
LL | / match Some(1) {
@ -39,7 +39,7 @@ LL | return
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
|
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>());
unsafe { y.as_ptr().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`
--> $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>()) };
| ^^^^^^^^^^^^^^^
@ -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
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])) };
| ^^^^^^^^^^^^^^^^^^
@ -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
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>()) };
| ^^^^^^^^^^^^^^^
@ -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
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>()) };
| ^^^^^^^^^^^^^^^
@ -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
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>()) };
| ^^^^^^^^^^^^^^^
@ -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
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>()) };
| ^^^^^^^^^^^^^^^
@ -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
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>()) };
| ^^^^^^^^^^^^^^^
@ -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
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])) };
| ^^^^^^^^^^^^^^^^^^
@ -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
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) };
| ^^^^^^^^^^^^^^^^^^^^^^
@ -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
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) };
| ^^^^^^^^^^^^^^^^^^^^^^
@ -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
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) };
| ^^^^^^^^^^^^^^^^^^^^^^
@ -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
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);
| ^^^^^^^^^^^^^^^^^^^^^^
@ -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
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);
| ^^^^^^^^^^^^^^^^^^^^^^
@ -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
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) };
| ^^^^^^^^^^^^^^^^^^^^^^
@ -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
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) };
| ^^^^^^^^^^^^^^^^^^^^^^
@ -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
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>()) };
| ^^^^^^^^^^^^^^^
@ -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
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>());
| ^^^^^^^^^^^^^^^
@ -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
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>()) };
| ^^^^^^^^^^^^^^^
@ -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
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>());
| ^^^^^^^^^^^^^^^
@ -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
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) };
| ^^^^^^^^^^^^^^^^^^^^^^^^
@ -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
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);
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= 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: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
error: aborting due to 21 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
|
LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
= 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
|
LL | vec.sort();
| ^^^^^^^^^^ 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
|
LL | vec.sort();
| ^^^^^^^^^^ 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
|
LL | vec.sort();
| ^^^^^^^^^^ 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
|
LL | vec.sort();
| ^^^^^^^^^^ 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
|
LL | vec.sort();
| ^^^^^^^^^^ 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
|
LL | arr.sort();
| ^^^^^^^^^^ 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

View File

@ -27,7 +27,7 @@ fn buggy_ab_cmp(s1: &S, s2: &S) -> bool {
s1.a < s2.a && s1.a < s2.b
}
struct SAOnly {
struct SaOnly {
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
// `s2.b` since that is invalid.
s1.a < s2.a && s1.a() < s1.b
}
fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SAOnly) -> bool {
fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SaOnly) -> bool {
macro_rules! s1 {
() => {
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
}
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
s1.a < s2.a && s1.b < s1.b
}

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