Auto merge of #72972 - lzutao:clippy, r=Manishearth
Pull changes from rust-lang/rust-clippy
This commit is contained in:
commit
283522400b
@ -232,7 +232,8 @@ jobs:
|
||||
matrix:
|
||||
integration:
|
||||
- 'rust-lang/cargo'
|
||||
- 'rust-lang/rls'
|
||||
# FIXME: re-enable once fmt_macros is renamed in RLS
|
||||
# - 'rust-lang/rls'
|
||||
- 'rust-lang/chalk'
|
||||
- 'rust-lang/rustfmt'
|
||||
- 'Marwes/combine'
|
||||
|
@ -6,11 +6,88 @@ document.
|
||||
|
||||
## Unreleased / In Rust Nightly
|
||||
|
||||
[891e1a8...master](https://github.com/rust-lang/rust-clippy/compare/891e1a8...master)
|
||||
[7ea7cd1...master](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master)
|
||||
|
||||
## Rust 1.45
|
||||
|
||||
Current beta, release 2020-07-16
|
||||
|
||||
[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
|
||||
|
||||
### New lints
|
||||
|
||||
* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582)
|
||||
* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493)
|
||||
* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332)
|
||||
* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506)
|
||||
* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439)
|
||||
* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522)
|
||||
* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576)
|
||||
* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583)
|
||||
* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550)
|
||||
|
||||
### Moves and Deprecations
|
||||
|
||||
* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408)
|
||||
* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622)
|
||||
* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599)
|
||||
* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529)
|
||||
* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568)
|
||||
* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`].
|
||||
[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
|
||||
* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`].
|
||||
[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
|
||||
* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`].
|
||||
[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
|
||||
* Merge `option_expect_used` and `result_expect_used` into [`expect_used`].
|
||||
[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
|
||||
* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`].
|
||||
[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505)
|
||||
* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631)
|
||||
* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592)
|
||||
* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616)
|
||||
|
||||
### False Positive Fixes
|
||||
|
||||
* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525)
|
||||
* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609)
|
||||
* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558)
|
||||
* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596)
|
||||
* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535)
|
||||
* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491)
|
||||
* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602)
|
||||
* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564)
|
||||
* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611)
|
||||
* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636)
|
||||
* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647)
|
||||
* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`]
|
||||
and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651)
|
||||
* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429)
|
||||
|
||||
### Suggestion Improvements
|
||||
|
||||
* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536)
|
||||
* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511)
|
||||
* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530)
|
||||
* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547)
|
||||
|
||||
### ICE Fixes
|
||||
|
||||
* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590)
|
||||
* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499)
|
||||
|
||||
### Documentation
|
||||
|
||||
* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639)
|
||||
* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541)
|
||||
|
||||
## Rust 1.44
|
||||
|
||||
Current beta, release 2020-06-04
|
||||
Current stable, released 2020-06-04
|
||||
|
||||
[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
|
||||
|
||||
@ -93,7 +170,7 @@ Current beta, release 2020-06-04
|
||||
|
||||
## Rust 1.43
|
||||
|
||||
Current stable, released 2020-04-23
|
||||
Released 2020-04-23
|
||||
|
||||
[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
|
||||
|
||||
@ -1401,6 +1478,7 @@ Released 2018-09-13
|
||||
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
|
||||
[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
|
||||
[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
|
||||
[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
|
||||
[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
|
||||
[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
|
||||
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
|
||||
@ -1601,9 +1679,11 @@ Released 2018-09-13
|
||||
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
|
||||
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
|
||||
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
|
||||
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
|
||||
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
|
||||
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
|
||||
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
|
||||
[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
|
||||
[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
|
||||
[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
|
||||
[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
|
||||
@ -1630,6 +1710,7 @@ Released 2018-09-13
|
||||
[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
|
||||
[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
|
||||
[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
|
||||
[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero
|
||||
[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
|
||||
[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
|
||||
[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
|
||||
|
@ -12,14 +12,16 @@ anything, feel free to ask questions on issues or visit the `#clippy` on [Discor
|
||||
|
||||
All contributors are expected to follow the [Rust Code of Conduct].
|
||||
|
||||
* [Getting started](#getting-started)
|
||||
* [Finding something to fix/improve](#finding-something-to-fiximprove)
|
||||
* [Writing code](#writing-code)
|
||||
* [How Clippy works](#how-clippy-works)
|
||||
* [Fixing nightly build failures](#fixing-build-failures-caused-by-rust)
|
||||
* [Issue and PR Triage](#issue-and-pr-triage)
|
||||
* [Bors and Homu](#bors-and-homu)
|
||||
* [Contributions](#contributions)
|
||||
- [Contributing to Clippy](#contributing-to-clippy)
|
||||
- [Getting started](#getting-started)
|
||||
- [Finding something to fix/improve](#finding-something-to-fiximprove)
|
||||
- [Writing code](#writing-code)
|
||||
- [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
|
||||
- [How Clippy works](#how-clippy-works)
|
||||
- [Fixing build failures caused by Rust](#fixing-build-failures-caused-by-rust)
|
||||
- [Issue and PR triage](#issue-and-pr-triage)
|
||||
- [Bors and Homu](#bors-and-homu)
|
||||
- [Contributions](#contributions)
|
||||
|
||||
[Discord]: https://discord.gg/rust-lang
|
||||
[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct
|
||||
@ -91,6 +93,24 @@ quick read.
|
||||
[rfc_stability]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#stability-guarantees
|
||||
[rfc_lint_cats]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories
|
||||
|
||||
## Getting code-completion for rustc internals to work
|
||||
|
||||
Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals
|
||||
using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not
|
||||
available via a `rustup` component at the time of writing.
|
||||
To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via
|
||||
`git clone https://github.com/rust-lang/rust/`.
|
||||
Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
|
||||
which rust-analyzer will be able to understand.
|
||||
Run `cargo dev ra-setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo
|
||||
you just cloned.
|
||||
The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
|
||||
Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses.
|
||||
Just make sure to remove the dependencies again before finally making a pull request!
|
||||
|
||||
[ra_homepage]: https://rust-analyzer.github.io/
|
||||
[rustc_repo]: https://github.com/rust-lang/rust/
|
||||
|
||||
## How Clippy works
|
||||
|
||||
[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers in the [`LintStore`].
|
||||
|
@ -37,7 +37,7 @@ tempfile = { version = "3.1.0", optional = true }
|
||||
lazy_static = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
cargo_metadata = "0.9.0"
|
||||
cargo_metadata = "0.9.1"
|
||||
compiletest_rs = { version = "0.5.0", features = ["tmp"] }
|
||||
tester = "0.7"
|
||||
lazy_static = "1.0"
|
||||
|
@ -11,6 +11,7 @@ use walkdir::WalkDir;
|
||||
|
||||
pub mod fmt;
|
||||
pub mod new_lint;
|
||||
pub mod ra_setup;
|
||||
pub mod stderr_length_check;
|
||||
pub mod update_lints;
|
||||
|
||||
@ -400,7 +401,7 @@ fn test_replace_region_no_changes() {
|
||||
changed: false,
|
||||
new_lines: "123\n456\n789".to_string(),
|
||||
};
|
||||
let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, || vec![]);
|
||||
let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new);
|
||||
assert_eq!(expected, result);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
|
||||
use clap::{App, Arg, SubCommand};
|
||||
use clippy_dev::{fmt, new_lint, stderr_length_check, update_lints};
|
||||
use clippy_dev::{fmt, new_lint, ra_setup, stderr_length_check, update_lints};
|
||||
|
||||
fn main() {
|
||||
let matches = App::new("Clippy developer tooling")
|
||||
@ -87,6 +87,19 @@ fn main() {
|
||||
SubCommand::with_name("limit_stderr_length")
|
||||
.about("Ensures that stderr files do not grow longer than a certain amount of lines."),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("ra-setup")
|
||||
.about("Alter dependencies so rust-analyzer can find rustc internals")
|
||||
.arg(
|
||||
Arg::with_name("rustc-repo-path")
|
||||
.long("repo-path")
|
||||
.short("r")
|
||||
.help("The path to a rustc repo that will be used for setting the dependencies")
|
||||
.takes_value(true)
|
||||
.value_name("path")
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
match matches.subcommand() {
|
||||
@ -115,6 +128,7 @@ fn main() {
|
||||
("limit_stderr_length", _) => {
|
||||
stderr_length_check::check();
|
||||
},
|
||||
("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -147,6 +147,8 @@ fn get_manifest_contents(lint_name: &str, hint: &str) -> String {
|
||||
name = "{}"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
"#,
|
||||
hint, lint_name
|
||||
)
|
||||
|
90
src/tools/clippy/clippy_dev/src/ra_setup.rs
Normal file
90
src/tools/clippy/clippy_dev/src/ra_setup.rs
Normal file
@ -0,0 +1,90 @@
|
||||
#![allow(clippy::filter_map)]
|
||||
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
|
||||
// the respective rustc subcrates instead of using extern crate xyz.
|
||||
// This allows rust analyzer to analyze rustc internals and show proper information inside clippy
|
||||
// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details
|
||||
|
||||
pub fn run(rustc_path: Option<&str>) {
|
||||
// we can unwrap here because the arg is required here
|
||||
let rustc_path = PathBuf::from(rustc_path.unwrap());
|
||||
assert!(rustc_path.is_dir(), "path is not a directory");
|
||||
let rustc_source_basedir = rustc_path.join("src");
|
||||
assert!(
|
||||
rustc_source_basedir.is_dir(),
|
||||
"are you sure the path leads to a rustc repo?"
|
||||
);
|
||||
|
||||
let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml");
|
||||
let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs");
|
||||
inject_deps_into_manifest(
|
||||
&rustc_source_basedir,
|
||||
"Cargo.toml",
|
||||
&clippy_root_manifest,
|
||||
&clippy_root_lib_rs,
|
||||
)
|
||||
.expect("Failed to inject deps into ./Cargo.toml");
|
||||
|
||||
let clippy_lints_manifest =
|
||||
fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml");
|
||||
let clippy_lints_lib_rs =
|
||||
fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs");
|
||||
inject_deps_into_manifest(
|
||||
&rustc_source_basedir,
|
||||
"clippy_lints/Cargo.toml",
|
||||
&clippy_lints_manifest,
|
||||
&clippy_lints_lib_rs,
|
||||
)
|
||||
.expect("Failed to inject deps into ./clippy_lints/Cargo.toml");
|
||||
}
|
||||
|
||||
fn inject_deps_into_manifest(
|
||||
rustc_source_dir: &PathBuf,
|
||||
manifest_path: &str,
|
||||
cargo_toml: &str,
|
||||
lib_rs: &str,
|
||||
) -> std::io::Result<()> {
|
||||
let extern_crates = lib_rs
|
||||
.lines()
|
||||
// get the deps
|
||||
.filter(|line| line.starts_with("extern crate"))
|
||||
// we have something like "extern crate foo;", we only care about the "foo"
|
||||
// ↓ ↓
|
||||
// extern crate rustc_middle;
|
||||
.map(|s| &s[13..(s.len() - 1)]);
|
||||
|
||||
let new_deps = extern_crates.map(|dep| {
|
||||
// format the dependencies that are going to be put inside the Cargo.toml
|
||||
format!(
|
||||
"{dep} = {{ path = \"{source_path}/lib{dep}\" }}\n",
|
||||
dep = dep,
|
||||
source_path = rustc_source_dir.display()
|
||||
)
|
||||
});
|
||||
|
||||
// format a new [dependencies]-block with the new deps we need to inject
|
||||
let mut all_deps = String::from("[dependencies]\n");
|
||||
new_deps.for_each(|dep_line| {
|
||||
all_deps.push_str(&dep_line);
|
||||
});
|
||||
|
||||
// replace "[dependencies]" with
|
||||
// [dependencies]
|
||||
// dep1 = { path = ... }
|
||||
// dep2 = { path = ... }
|
||||
// etc
|
||||
let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);
|
||||
|
||||
// println!("{}", new_manifest);
|
||||
let mut file = File::create(manifest_path)?;
|
||||
file.write_all(new_manifest.as_bytes())?;
|
||||
|
||||
println!("Dependency paths injected: {}", manifest_path);
|
||||
|
||||
Ok(())
|
||||
}
|
@ -17,11 +17,11 @@ keywords = ["clippy", "lint", "plugin"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
cargo_metadata = "0.9.0"
|
||||
cargo_metadata = "0.9.1"
|
||||
if_chain = "1.0.0"
|
||||
itertools = "0.9"
|
||||
lazy_static = "1.0.2"
|
||||
pulldown-cmark = { version = "0.7", default-features = false }
|
||||
pulldown-cmark = { version = "0.7.1", default-features = false }
|
||||
quine-mc_cluskey = "0.2.2"
|
||||
regex-syntax = "0.6"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
@ -24,7 +24,11 @@ declare_clippy_lint! {
|
||||
/// let mut a = 5;
|
||||
/// let b = 0;
|
||||
/// // ...
|
||||
/// // Bad
|
||||
/// a = a + b;
|
||||
///
|
||||
/// // Good
|
||||
/// a += b;
|
||||
/// ```
|
||||
pub ASSIGN_OP_PATTERN,
|
||||
style,
|
||||
|
@ -54,18 +54,13 @@ declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]);
|
||||
impl LateLintPass<'_, '_> for AwaitHoldingLock {
|
||||
fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &'_ Body<'_>) {
|
||||
use AsyncGeneratorKind::{Block, Closure, Fn};
|
||||
match body.generator_kind {
|
||||
Some(GeneratorKind::Async(Block))
|
||||
| Some(GeneratorKind::Async(Closure))
|
||||
| Some(GeneratorKind::Async(Fn)) => {
|
||||
let body_id = BodyId {
|
||||
hir_id: body.value.hir_id,
|
||||
};
|
||||
let def_id = cx.tcx.hir().body_owner_def_id(body_id);
|
||||
let tables = cx.tcx.typeck_tables_of(def_id);
|
||||
check_interior_types(cx, &tables.generator_interior_types, body.value.span);
|
||||
},
|
||||
_ => {},
|
||||
if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
|
||||
let body_id = BodyId {
|
||||
hir_id: body.value.hir_id,
|
||||
};
|
||||
let def_id = cx.tcx.hir().body_owner_def_id(body_id);
|
||||
let tables = cx.tcx.typeck_tables_of(def_id);
|
||||
check_interior_types(cx, &tables.generator_interior_types, body.value.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,13 +36,9 @@ declare_clippy_lint! {
|
||||
"common metadata is defined in `Cargo.toml`"
|
||||
}
|
||||
|
||||
fn warning(cx: &LateContext<'_, '_>, message: &str) {
|
||||
span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message);
|
||||
}
|
||||
|
||||
fn missing_warning(cx: &LateContext<'_, '_>, package: &cargo_metadata::Package, field: &str) {
|
||||
let message = format!("package `{}` is missing `{}` metadata", package.name, field);
|
||||
warning(cx, &message);
|
||||
span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message);
|
||||
}
|
||||
|
||||
fn is_empty_str(value: &Option<String>) -> bool {
|
||||
@ -66,12 +62,7 @@ impl LateLintPass<'_, '_> for CargoCommonMetadata {
|
||||
return;
|
||||
}
|
||||
|
||||
let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() {
|
||||
metadata
|
||||
} else {
|
||||
warning(cx, "could not read cargo metadata");
|
||||
return;
|
||||
};
|
||||
let metadata = unwrap_cargo_metadata!(cx, CARGO_COMMON_METADATA, false);
|
||||
|
||||
for package in metadata.packages {
|
||||
if is_empty_vec(&package.authors) {
|
||||
|
@ -58,24 +58,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CheckedConversions {
|
||||
}
|
||||
};
|
||||
|
||||
if_chain! {
|
||||
if let Some(cv) = result;
|
||||
if let Some(to_type) = cv.to_type;
|
||||
|
||||
then {
|
||||
if let Some(cv) = result {
|
||||
if let Some(to_type) = cv.to_type {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut
|
||||
applicability);
|
||||
let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CHECKED_CONVERSIONS,
|
||||
item.span,
|
||||
"Checked cast can be simplified.",
|
||||
"try",
|
||||
format!("{}::try_from({}).is_ok()",
|
||||
to_type,
|
||||
snippet),
|
||||
applicability
|
||||
format!("{}::try_from({}).is_ok()", to_type, snippet),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -184,7 +178,7 @@ fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind;
|
||||
if let Some((candidate, check)) = normalize_le_ge(op, left, right);
|
||||
if let Some((from, to)) = get_types_from_cast(check, MAX_VALUE, INTS);
|
||||
if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX");
|
||||
|
||||
then {
|
||||
Conversion::try_new(candidate, from, to)
|
||||
@ -224,7 +218,7 @@ fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> O
|
||||
|
||||
/// Check for `expr >= (to_type::MIN as from_type)`
|
||||
fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
|
||||
if let Some((from, to)) = get_types_from_cast(check, MIN_VALUE, SINTS) {
|
||||
if let Some((from, to)) = get_types_from_cast(check, SINTS, "min_value", "MIN") {
|
||||
Conversion::try_new(candidate, from, to)
|
||||
} else {
|
||||
None
|
||||
@ -232,10 +226,16 @@ fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Op
|
||||
}
|
||||
|
||||
/// Tries to extract the from- and to-type from a cast expression
|
||||
fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) -> Option<(&'a str, &'a str)> {
|
||||
// `to_type::maxmin_value() as from_type`
|
||||
fn get_types_from_cast<'a>(
|
||||
expr: &'a Expr<'_>,
|
||||
types: &'a [&str],
|
||||
func: &'a str,
|
||||
assoc_const: &'a str,
|
||||
) -> Option<(&'a str, &'a str)> {
|
||||
// `to_type::max_value() as from_type`
|
||||
// or `to_type::MAX as from_type`
|
||||
let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! {
|
||||
// to_type::maxmin_value(), from_type
|
||||
// to_type::max_value(), from_type
|
||||
if let ExprKind::Cast(ref limit, ref from_type) = &expr.kind;
|
||||
if let TyKind::Path(ref from_type_path) = &from_type.kind;
|
||||
if let Some(from_sym) = int_ty_to_sym(from_type_path);
|
||||
@ -247,17 +247,17 @@ fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str])
|
||||
}
|
||||
};
|
||||
|
||||
// `from_type::from(to_type::maxmin_value())`
|
||||
// `from_type::from(to_type::max_value())`
|
||||
let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| {
|
||||
if_chain! {
|
||||
// `from_type::from, to_type::maxmin_value()`
|
||||
// `from_type::from, to_type::max_value()`
|
||||
if let ExprKind::Call(ref from_func, ref args) = &expr.kind;
|
||||
// `to_type::maxmin_value()`
|
||||
// `to_type::max_value()`
|
||||
if args.len() == 1;
|
||||
if let limit = &args[0];
|
||||
// `from_type::from`
|
||||
if let ExprKind::Path(ref path) = &from_func.kind;
|
||||
if let Some(from_sym) = get_implementing_type(path, INTS, FROM);
|
||||
if let Some(from_sym) = get_implementing_type(path, INTS, "from");
|
||||
|
||||
then {
|
||||
Some((limit, from_sym))
|
||||
@ -268,22 +268,26 @@ fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str])
|
||||
});
|
||||
|
||||
if let Some((limit, from_type)) = limit_from {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(ref fun_name, _) = &limit.kind;
|
||||
// `to_type, maxmin_value`
|
||||
if let ExprKind::Path(ref path) = &fun_name.kind;
|
||||
// `to_type`
|
||||
if let Some(to_type) = get_implementing_type(path, types, func);
|
||||
|
||||
then {
|
||||
Some((from_type, to_type))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
match limit.kind {
|
||||
// `from_type::from(_)`
|
||||
ExprKind::Call(path, _) => {
|
||||
if let ExprKind::Path(ref path) = path.kind {
|
||||
// `to_type`
|
||||
if let Some(to_type) = get_implementing_type(path, types, func) {
|
||||
return Some((from_type, to_type));
|
||||
}
|
||||
}
|
||||
},
|
||||
// `to_type::MAX`
|
||||
ExprKind::Path(ref path) => {
|
||||
if let Some(to_type) = get_implementing_type(path, types, assoc_const) {
|
||||
return Some((from_type, to_type));
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
None
|
||||
}
|
||||
|
||||
/// Gets the type which implements the called function
|
||||
@ -336,10 +340,6 @@ fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> O
|
||||
}
|
||||
|
||||
// Constants
|
||||
const FROM: &str = "from";
|
||||
const MAX_VALUE: &str = "max_value";
|
||||
const MIN_VALUE: &str = "min_value";
|
||||
|
||||
const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"];
|
||||
const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"];
|
||||
const INTS: &[&str] = &["u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize"];
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::utils::{get_parent_expr, higher, if_sequence, same_tys, snippet, span_lint_and_note, span_lint_and_then};
|
||||
use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then};
|
||||
use crate::utils::{SpanlessEq, SpanlessHash};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::{Ty, TyS};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use std::collections::hash_map::Entry;
|
||||
@ -242,15 +242,11 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_, '_>, conds: &[&Expr<'_>]) {
|
||||
|
||||
/// Implementation of `MATCH_SAME_ARMS`.
|
||||
fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>) {
|
||||
fn same_bindings<'tcx>(
|
||||
cx: &LateContext<'_, 'tcx>,
|
||||
lhs: &FxHashMap<Symbol, Ty<'tcx>>,
|
||||
rhs: &FxHashMap<Symbol, Ty<'tcx>>,
|
||||
) -> bool {
|
||||
fn same_bindings<'tcx>(lhs: &FxHashMap<Symbol, Ty<'tcx>>, rhs: &FxHashMap<Symbol, Ty<'tcx>>) -> bool {
|
||||
lhs.len() == rhs.len()
|
||||
&& lhs
|
||||
.iter()
|
||||
.all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| same_tys(cx, l_ty, r_ty)))
|
||||
.all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty)))
|
||||
}
|
||||
|
||||
if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
|
||||
@ -269,7 +265,7 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>) {
|
||||
(min_index..=max_index).all(|index| arms[index].guard.is_none()) &&
|
||||
SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) &&
|
||||
// all patterns should have the same bindings
|
||||
same_bindings(cx, &bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat))
|
||||
same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat))
|
||||
};
|
||||
|
||||
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
|
||||
|
@ -13,10 +13,24 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// fn simple_double_parens() -> i32 {
|
||||
/// ((0))
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// fn simple_no_parens() -> i32 {
|
||||
/// 0
|
||||
/// }
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// # fn foo(bar: usize) {}
|
||||
/// ((0));
|
||||
/// // Bad
|
||||
/// foo((0));
|
||||
/// ((1, 2));
|
||||
///
|
||||
/// // Good
|
||||
/// foo(0);
|
||||
/// ```
|
||||
pub DOUBLE_PARENS,
|
||||
complexity,
|
||||
|
@ -27,6 +27,10 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// fn foo<T: Drop>() {}
|
||||
/// ```
|
||||
/// Could be written as:
|
||||
/// ```rust
|
||||
/// fn foo<T>() {}
|
||||
/// ```
|
||||
pub DROP_BOUNDS,
|
||||
correctness,
|
||||
"Bounds of the form `T: Drop` are useless"
|
||||
|
@ -22,8 +22,14 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # use std::time::Duration;
|
||||
/// let dur = Duration::new(5, 0);
|
||||
///
|
||||
/// // Bad
|
||||
/// let _micros = dur.subsec_nanos() / 1_000;
|
||||
/// let _millis = dur.subsec_nanos() / 1_000_000;
|
||||
///
|
||||
/// // Good
|
||||
/// let _micros = dur.subsec_micros();
|
||||
/// let _millis = dur.subsec_millis();
|
||||
/// ```
|
||||
pub DURATION_SUBSEC,
|
||||
complexity,
|
||||
|
@ -25,31 +25,47 @@ declare_clippy_lint! {
|
||||
/// BattenbergCake,
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be written as:
|
||||
/// ```rust
|
||||
/// enum Cake {
|
||||
/// BlackForest,
|
||||
/// Hummingbird,
|
||||
/// Battenberg,
|
||||
/// }
|
||||
/// ```
|
||||
pub ENUM_VARIANT_NAMES,
|
||||
style,
|
||||
"enums where all variants share a prefix/postfix"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Detects enumeration variants that are prefixed or suffixed
|
||||
/// by the same characters.
|
||||
/// **What it does:** Detects public enumeration variants that are
|
||||
/// prefixed or suffixed by the same characters.
|
||||
///
|
||||
/// **Why is this bad?** Enumeration variant names should specify their variant,
|
||||
/// **Why is this bad?** Public enumeration variant names should specify their variant,
|
||||
/// not repeat the enumeration name.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// enum Cake {
|
||||
/// pub enum Cake {
|
||||
/// BlackForestCake,
|
||||
/// HummingbirdCake,
|
||||
/// BattenbergCake,
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be written as:
|
||||
/// ```rust
|
||||
/// pub enum Cake {
|
||||
/// BlackForest,
|
||||
/// Hummingbird,
|
||||
/// Battenberg,
|
||||
/// }
|
||||
/// ```
|
||||
pub PUB_ENUM_VARIANT_NAMES,
|
||||
pedantic,
|
||||
"enums where all variants share a prefix/postfix"
|
||||
"public enums where all variants share a prefix/postfix"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -66,6 +82,12 @@ declare_clippy_lint! {
|
||||
/// struct BlackForestCake;
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be written as:
|
||||
/// ```rust
|
||||
/// mod cake {
|
||||
/// struct BlackForest;
|
||||
/// }
|
||||
/// ```
|
||||
pub MODULE_NAME_REPETITIONS,
|
||||
pedantic,
|
||||
"type names prefixed/postfixed with their containing module's name"
|
||||
|
@ -39,7 +39,11 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// // Bad
|
||||
/// &x == y
|
||||
///
|
||||
/// // Good
|
||||
/// x == *y
|
||||
/// ```
|
||||
pub OP_REF,
|
||||
style,
|
||||
|
@ -28,9 +28,16 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # fn foo(bar: usize) {}
|
||||
///
|
||||
/// // Bad
|
||||
/// let x = Box::new(1);
|
||||
/// foo(*x);
|
||||
/// println!("{}", *x);
|
||||
///
|
||||
/// // Good
|
||||
/// let x = 1;
|
||||
/// foo(x);
|
||||
/// println!("{}", x);
|
||||
/// ```
|
||||
pub BOXED_LOCAL,
|
||||
perf,
|
||||
|
@ -26,7 +26,11 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// xs.map(|x| foo(x))
|
||||
///
|
||||
/// // Good
|
||||
/// xs.map(foo)
|
||||
/// ```
|
||||
/// where `foo(_)` is a plain function that takes the exact argument type of
|
||||
/// `x`.
|
||||
|
@ -21,11 +21,20 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut x = 0;
|
||||
///
|
||||
/// // Bad
|
||||
/// let a = {
|
||||
/// x = 1;
|
||||
/// 1
|
||||
/// } + x;
|
||||
/// // Unclear whether a is 1 or 2.
|
||||
///
|
||||
/// // Good
|
||||
/// let tmp = {
|
||||
/// x = 1;
|
||||
/// 1
|
||||
/// };
|
||||
/// let a = tmp + x;
|
||||
/// ```
|
||||
pub EVAL_ORDER_DEPENDENCE,
|
||||
complexity,
|
||||
|
@ -20,12 +20,31 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// struct Foo(i32);
|
||||
///
|
||||
/// // Bad
|
||||
/// impl From<String> for Foo {
|
||||
/// fn from(s: String) -> Self {
|
||||
/// Foo(s.parse().unwrap())
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// // Good
|
||||
/// struct Foo(i32);
|
||||
///
|
||||
/// use std::convert::TryFrom;
|
||||
/// impl TryFrom<String> for Foo {
|
||||
/// type Error = ();
|
||||
/// fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
/// if let Ok(parsed) = s.parse() {
|
||||
/// Ok(Foo(parsed))
|
||||
/// } else {
|
||||
/// Err(())
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub FALLIBLE_IMPL_FROM,
|
||||
nursery,
|
||||
"Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
|
||||
@ -120,7 +139,7 @@ fn lint_impl_body<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, impl_span: Span, impl_it
|
||||
move |diag| {
|
||||
diag.help(
|
||||
"`From` is intended for infallible conversions only. \
|
||||
Use `TryFrom` if there's a possibility for the conversion to fail.");
|
||||
Use `TryFrom` if there's a possibility for the conversion to fail.");
|
||||
diag.span_note(fpu.result, "potential failure(s)");
|
||||
});
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
///
|
||||
/// let a = 3f32;
|
||||
/// let _ = a.powf(1.0 / 3.0);
|
||||
/// let _ = (1.0 + a).ln();
|
||||
@ -38,7 +37,6 @@ declare_clippy_lint! {
|
||||
/// is better expressed as
|
||||
///
|
||||
/// ```rust
|
||||
///
|
||||
/// let a = 3f32;
|
||||
/// let _ = a.cbrt();
|
||||
/// let _ = a.ln_1p();
|
||||
|
@ -25,9 +25,13 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Examples:**
|
||||
/// ```rust
|
||||
///
|
||||
/// // Bad
|
||||
/// # let foo = "foo";
|
||||
/// format!("foo");
|
||||
/// format!("{}", foo);
|
||||
///
|
||||
/// // Good
|
||||
/// format!("foo");
|
||||
/// ```
|
||||
pub USELESS_FORMAT,
|
||||
complexity,
|
||||
|
@ -112,12 +112,8 @@ declare_lint_pass!(Formatting => [
|
||||
impl EarlyLintPass for Formatting {
|
||||
fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
|
||||
for w in block.stmts.windows(2) {
|
||||
match (&w[0].kind, &w[1].kind) {
|
||||
(&StmtKind::Expr(ref first), &StmtKind::Expr(ref second))
|
||||
| (&StmtKind::Expr(ref first), &StmtKind::Semi(ref second)) => {
|
||||
check_missing_else(cx, first, second);
|
||||
},
|
||||
_ => (),
|
||||
if let (StmtKind::Expr(first), StmtKind::Expr(second) | StmtKind::Semi(second)) = (&w[0].kind, &w[1].kind) {
|
||||
check_missing_else(cx, first, second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,11 +49,11 @@ declare_clippy_lint! {
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ``` rust
|
||||
/// ```rust
|
||||
/// fn im_too_long() {
|
||||
/// println!("");
|
||||
/// // ... 100 more LoC
|
||||
/// println!("");
|
||||
/// println!("");
|
||||
/// // ... 100 more LoC
|
||||
/// println!("");
|
||||
/// }
|
||||
/// ```
|
||||
pub TOO_MANY_LINES,
|
||||
@ -79,10 +79,16 @@ declare_clippy_lint! {
|
||||
/// `some_argument.get_raw_ptr()`).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// pub fn foo(x: *const u8) {
|
||||
/// println!("{}", unsafe { *x });
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// pub unsafe fn foo(x: *const u8) {
|
||||
/// println!("{}", unsafe { *x });
|
||||
/// }
|
||||
/// ```
|
||||
pub NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
correctness,
|
||||
|
@ -25,13 +25,6 @@ declare_clippy_lint! {
|
||||
/// if i != 0 {
|
||||
/// i -= 1;
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let end: u32 = 10;
|
||||
/// let start: u32 = 5;
|
||||
///
|
||||
/// let mut i: u32 = end - start;
|
||||
///
|
||||
/// // Good
|
||||
/// i = i.saturating_sub(1);
|
||||
|
@ -10,7 +10,6 @@ use crate::utils::{snippet_opt, span_lint_and_sugg};
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
|
||||
///
|
||||
///
|
||||
/// **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
|
@ -15,10 +15,13 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let x = 3 / 2;
|
||||
/// println!("{}", x);
|
||||
/// }
|
||||
/// // Bad
|
||||
/// let x = 3 / 2;
|
||||
/// println!("{}", x);
|
||||
///
|
||||
/// // Good
|
||||
/// let x = 3f32 / 2f32;
|
||||
/// println!("{}", x);
|
||||
/// ```
|
||||
pub INTEGER_DIVISION,
|
||||
restriction,
|
||||
|
@ -16,6 +16,7 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// fn foo() {
|
||||
/// println!("cake");
|
||||
/// }
|
||||
@ -28,6 +29,21 @@ declare_clippy_lint! {
|
||||
/// foo(); // prints "foo"
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// // Good
|
||||
/// fn foo() {
|
||||
/// println!("cake");
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// fn foo() {
|
||||
/// println!("foo");
|
||||
/// }
|
||||
/// foo(); // prints "foo"
|
||||
/// foo(); // prints "foo"
|
||||
/// }
|
||||
/// ```
|
||||
pub ITEMS_AFTER_STATEMENTS,
|
||||
pedantic,
|
||||
"blocks where an item comes after a statement"
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
|
||||
use crate::utils::{get_item_name, higher, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
@ -259,6 +259,17 @@ fn check_len(
|
||||
|
||||
/// Checks if this type has an `is_empty` method.
|
||||
fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
|
||||
/// Special case ranges until `range_is_empty` is stabilized. See issue 3807.
|
||||
fn should_skip_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
|
||||
higher::range(cx, expr).map_or(false, |_| {
|
||||
!cx.tcx
|
||||
.features()
|
||||
.declared_lib_features
|
||||
.iter()
|
||||
.any(|(name, _)| name.as_str() == "range_is_empty")
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
|
||||
fn is_is_empty(cx: &LateContext<'_, '_>, item: &ty::AssocItem) -> bool {
|
||||
if let ty::AssocKind::Fn = item.kind {
|
||||
@ -284,6 +295,10 @@ fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
if should_skip_range(cx, expr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let ty = &walk_ptrs_ty(cx.tables.expr_ty(expr));
|
||||
match ty.kind {
|
||||
ty::Dynamic(ref tt, ..) => {
|
||||
|
141
src/tools/clippy/clippy_lints/src/let_and_return.rs
Normal file
141
src/tools/clippy/clippy_lints/src/let_and_return.rs
Normal file
@ -0,0 +1,141 @@
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `let`-bindings, which are subsequently
|
||||
/// returned.
|
||||
///
|
||||
/// **Why is this bad?** It is just extraneous code. Remove it to make your code
|
||||
/// more rusty.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo() -> String {
|
||||
/// let x = String::new();
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
/// instead, use
|
||||
/// ```
|
||||
/// fn foo() -> String {
|
||||
/// String::new()
|
||||
/// }
|
||||
/// ```
|
||||
pub LET_AND_RETURN,
|
||||
style,
|
||||
"creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
|
||||
}
|
||||
|
||||
declare_lint_pass!(LetReturn => [LET_AND_RETURN]);
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetReturn {
|
||||
fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) {
|
||||
// we need both a let-binding stmt and an expr
|
||||
if_chain! {
|
||||
if let Some(retexpr) = block.expr;
|
||||
if let Some(stmt) = block.stmts.iter().last();
|
||||
if let StmtKind::Local(local) = &stmt.kind;
|
||||
if local.ty.is_none();
|
||||
if local.attrs.is_empty();
|
||||
if let Some(initexpr) = &local.init;
|
||||
if let PatKind::Binding(.., ident, _) = local.pat.kind;
|
||||
if let ExprKind::Path(qpath) = &retexpr.kind;
|
||||
if match_qpath(qpath, &[&*ident.name.as_str()]);
|
||||
if !last_statement_borrows(cx, initexpr);
|
||||
if !in_external_macro(cx.sess(), initexpr.span);
|
||||
if !in_external_macro(cx.sess(), retexpr.span);
|
||||
if !in_external_macro(cx.sess(), local.span);
|
||||
if !in_macro(local.span);
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
LET_AND_RETURN,
|
||||
retexpr.span,
|
||||
"returning the result of a `let` binding from a block",
|
||||
|err| {
|
||||
err.span_label(local.span, "unnecessary `let` binding");
|
||||
|
||||
if let Some(snippet) = snippet_opt(cx, initexpr.span) {
|
||||
err.multipart_suggestion(
|
||||
"return the expression directly",
|
||||
vec![
|
||||
(local.span, String::new()),
|
||||
(retexpr.span, snippet),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.span_help(initexpr.span, "this expression can be directly returned");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn last_statement_borrows<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
let mut visitor = BorrowVisitor { cx, borrows: false };
|
||||
walk_expr(&mut visitor, expr);
|
||||
visitor.borrows
|
||||
}
|
||||
|
||||
struct BorrowVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
borrows: bool,
|
||||
}
|
||||
|
||||
impl BorrowVisitor<'_, '_> {
|
||||
fn fn_def_id(&self, expr: &Expr<'_>) -> Option<DefId> {
|
||||
match &expr.kind {
|
||||
ExprKind::MethodCall(..) => self.cx.tables.type_dependent_def_id(expr.hir_id),
|
||||
ExprKind::Call(
|
||||
Expr {
|
||||
kind: ExprKind::Path(qpath),
|
||||
..
|
||||
},
|
||||
..,
|
||||
) => self.cx.tables.qpath_res(qpath, expr.hir_id).opt_def_id(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if self.borrows {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(def_id) = self.fn_def_id(expr) {
|
||||
self.borrows = self
|
||||
.cx
|
||||
.tcx
|
||||
.fn_sig(def_id)
|
||||
.output()
|
||||
.skip_binder()
|
||||
.walk()
|
||||
.any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
|
||||
}
|
||||
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
// error-pattern:cargo-clippy
|
||||
|
||||
#![feature(bindings_after_at)]
|
||||
#![feature(box_syntax)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(or_patterns)]
|
||||
@ -12,6 +13,7 @@
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(concat_idents)]
|
||||
#![feature(drain_filter)]
|
||||
|
||||
// FIXME: switch to something more ergonomic here, once available.
|
||||
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
|
||||
@ -239,6 +241,7 @@ mod large_const_arrays;
|
||||
mod large_enum_variant;
|
||||
mod large_stack_arrays;
|
||||
mod len_zero;
|
||||
mod let_and_return;
|
||||
mod let_if_seq;
|
||||
mod let_underscore;
|
||||
mod lifetimes;
|
||||
@ -318,6 +321,8 @@ mod try_err;
|
||||
mod types;
|
||||
mod unicode;
|
||||
mod unnamed_address;
|
||||
mod unnecessary_sort_by;
|
||||
mod unnested_or_patterns;
|
||||
mod unsafe_removed_from_name;
|
||||
mod unused_io_amount;
|
||||
mod unused_self;
|
||||
@ -325,6 +330,7 @@ mod unwrap;
|
||||
mod use_self;
|
||||
mod useless_conversion;
|
||||
mod vec;
|
||||
mod vec_resize_to_zero;
|
||||
mod verbose_file_reads;
|
||||
mod wildcard_dependencies;
|
||||
mod wildcard_imports;
|
||||
@ -594,6 +600,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&large_stack_arrays::LARGE_STACK_ARRAYS,
|
||||
&len_zero::LEN_WITHOUT_IS_EMPTY,
|
||||
&len_zero::LEN_ZERO,
|
||||
&let_and_return::LET_AND_RETURN,
|
||||
&let_if_seq::USELESS_LET_IF_SEQ,
|
||||
&let_underscore::LET_UNDERSCORE_LOCK,
|
||||
&let_underscore::LET_UNDERSCORE_MUST_USE,
|
||||
@ -664,6 +671,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&methods::INTO_ITER_ON_REF,
|
||||
&methods::ITERATOR_STEP_BY_ZERO,
|
||||
&methods::ITER_CLONED_COLLECT,
|
||||
&methods::ITER_NEXT_SLICE,
|
||||
&methods::ITER_NTH,
|
||||
&methods::ITER_NTH_ZERO,
|
||||
&methods::ITER_SKIP_NEXT,
|
||||
@ -769,7 +777,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
®ex::INVALID_REGEX,
|
||||
®ex::REGEX_MACRO,
|
||||
®ex::TRIVIAL_REGEX,
|
||||
&returns::LET_AND_RETURN,
|
||||
&returns::NEEDLESS_RETURN,
|
||||
&returns::UNUSED_UNIT,
|
||||
&serde_api::SERDE_API_MISUSE,
|
||||
@ -832,6 +839,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&unicode::ZERO_WIDTH_SPACE,
|
||||
&unnamed_address::FN_ADDRESS_COMPARISONS,
|
||||
&unnamed_address::VTABLE_ADDRESS_COMPARISONS,
|
||||
&unnecessary_sort_by::UNNECESSARY_SORT_BY,
|
||||
&unnested_or_patterns::UNNESTED_OR_PATTERNS,
|
||||
&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
|
||||
&unused_io_amount::UNUSED_IO_AMOUNT,
|
||||
&unused_self::UNUSED_SELF,
|
||||
@ -847,6 +856,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&utils::internal_lints::OUTER_EXPN_EXPN_DATA,
|
||||
&utils::internal_lints::PRODUCE_ICE,
|
||||
&vec::USELESS_VEC,
|
||||
&vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
|
||||
&verbose_file_reads::VERBOSE_FILE_READS,
|
||||
&wildcard_dependencies::WILDCARD_DEPENDENCIES,
|
||||
&wildcard_imports::ENUM_GLOB_USE,
|
||||
@ -994,6 +1004,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast);
|
||||
store.register_late_pass(|| box redundant_clone::RedundantClone);
|
||||
store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit);
|
||||
store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy);
|
||||
store.register_late_pass(|| box types::RefToMut);
|
||||
store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants);
|
||||
store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn);
|
||||
@ -1016,6 +1027,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_early_pass(|| box formatting::Formatting);
|
||||
store.register_early_pass(|| box misc_early::MiscEarlyLints);
|
||||
store.register_early_pass(|| box returns::Return);
|
||||
store.register_late_pass(|| box let_and_return::LetReturn);
|
||||
store.register_early_pass(|| box collapsible_if::CollapsibleIf);
|
||||
store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
|
||||
store.register_early_pass(|| box precedence::Precedence);
|
||||
@ -1062,10 +1074,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
|
||||
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
|
||||
store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
|
||||
store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
|
||||
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
|
||||
store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
|
||||
single_char_binding_names_threshold,
|
||||
});
|
||||
store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns);
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
|
||||
@ -1164,6 +1178,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&types::CAST_POSSIBLE_TRUNCATION),
|
||||
LintId::of(&types::CAST_POSSIBLE_WRAP),
|
||||
LintId::of(&types::CAST_PRECISION_LOSS),
|
||||
LintId::of(&types::CAST_PTR_ALIGNMENT),
|
||||
LintId::of(&types::CAST_SIGN_LOSS),
|
||||
LintId::of(&types::IMPLICIT_HASHER),
|
||||
LintId::of(&types::INVALID_UPCAST_COMPARISONS),
|
||||
@ -1257,6 +1272,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
|
||||
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
|
||||
LintId::of(&len_zero::LEN_ZERO),
|
||||
LintId::of(&let_and_return::LET_AND_RETURN),
|
||||
LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
|
||||
LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES),
|
||||
LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
|
||||
@ -1303,6 +1319,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&methods::INTO_ITER_ON_REF),
|
||||
LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
|
||||
LintId::of(&methods::ITER_CLONED_COLLECT),
|
||||
LintId::of(&methods::ITER_NEXT_SLICE),
|
||||
LintId::of(&methods::ITER_NTH),
|
||||
LintId::of(&methods::ITER_NTH_ZERO),
|
||||
LintId::of(&methods::ITER_SKIP_NEXT),
|
||||
@ -1381,7 +1398,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(®ex::INVALID_REGEX),
|
||||
LintId::of(®ex::REGEX_MACRO),
|
||||
LintId::of(®ex::TRIVIAL_REGEX),
|
||||
LintId::of(&returns::LET_AND_RETURN),
|
||||
LintId::of(&returns::NEEDLESS_RETURN),
|
||||
LintId::of(&returns::UNUSED_UNIT),
|
||||
LintId::of(&serde_api::SERDE_API_MISUSE),
|
||||
@ -1410,7 +1426,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(&types::BORROWED_BOX),
|
||||
LintId::of(&types::BOX_VEC),
|
||||
LintId::of(&types::CAST_PTR_ALIGNMENT),
|
||||
LintId::of(&types::CAST_REF_TO_MUT),
|
||||
LintId::of(&types::CHAR_LIT_AS_U8),
|
||||
LintId::of(&types::FN_TO_NUMERIC_CAST),
|
||||
@ -1424,12 +1439,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&unicode::ZERO_WIDTH_SPACE),
|
||||
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
|
||||
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
|
||||
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
|
||||
LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
|
||||
LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
|
||||
LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
|
||||
LintId::of(&unwrap::PANICKING_UNWRAP),
|
||||
LintId::of(&unwrap::UNNECESSARY_UNWRAP),
|
||||
LintId::of(&useless_conversion::USELESS_CONVERSION),
|
||||
LintId::of(&vec::USELESS_VEC),
|
||||
LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
|
||||
LintId::of(&write::PRINTLN_EMPTY_STRING),
|
||||
LintId::of(&write::PRINT_LITERAL),
|
||||
LintId::of(&write::PRINT_WITH_NEWLINE),
|
||||
@ -1464,6 +1482,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&inherent_to_string::INHERENT_TO_STRING),
|
||||
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
|
||||
LintId::of(&len_zero::LEN_ZERO),
|
||||
LintId::of(&let_and_return::LET_AND_RETURN),
|
||||
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
|
||||
LintId::of(&loops::EMPTY_LOOP),
|
||||
LintId::of(&loops::FOR_KV_MAP),
|
||||
@ -1483,6 +1502,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&methods::CHARS_NEXT_CMP),
|
||||
LintId::of(&methods::INTO_ITER_ON_REF),
|
||||
LintId::of(&methods::ITER_CLONED_COLLECT),
|
||||
LintId::of(&methods::ITER_NEXT_SLICE),
|
||||
LintId::of(&methods::ITER_NTH_ZERO),
|
||||
LintId::of(&methods::ITER_SKIP_NEXT),
|
||||
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
@ -1515,7 +1535,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
|
||||
LintId::of(®ex::REGEX_MACRO),
|
||||
LintId::of(®ex::TRIVIAL_REGEX),
|
||||
LintId::of(&returns::LET_AND_RETURN),
|
||||
LintId::of(&returns::NEEDLESS_RETURN),
|
||||
LintId::of(&returns::UNUSED_UNIT),
|
||||
LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
||||
@ -1604,6 +1623,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&types::UNIT_ARG),
|
||||
LintId::of(&types::UNNECESSARY_CAST),
|
||||
LintId::of(&types::VEC_BOX),
|
||||
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
|
||||
LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
|
||||
LintId::of(&unwrap::UNNECESSARY_UNWRAP),
|
||||
LintId::of(&useless_conversion::USELESS_CONVERSION),
|
||||
LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO),
|
||||
@ -1669,7 +1690,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&transmute::WRONG_TRANSMUTE),
|
||||
LintId::of(&transmuting_null::TRANSMUTING_NULL),
|
||||
LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(&types::CAST_PTR_ALIGNMENT),
|
||||
LintId::of(&types::CAST_REF_TO_MUT),
|
||||
LintId::of(&types::UNIT_CMP),
|
||||
LintId::of(&unicode::ZERO_WIDTH_SPACE),
|
||||
@ -1677,6 +1697,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
|
||||
LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
|
||||
LintId::of(&unwrap::PANICKING_UNWRAP),
|
||||
LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
|
||||
]);
|
||||
|
||||
store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
|
||||
|
@ -24,7 +24,11 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x: u64 = 61864918973511;
|
||||
///
|
||||
/// // Good
|
||||
/// let x: u64 = 61_864_918_973_511;
|
||||
/// ```
|
||||
pub UNREADABLE_LITERAL,
|
||||
pedantic,
|
||||
@ -44,7 +48,11 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // Probably mistyped
|
||||
/// 2_32;
|
||||
///
|
||||
/// // Good
|
||||
/// 2_i32;
|
||||
/// ```
|
||||
pub MISTYPED_LITERAL_SUFFIXES,
|
||||
correctness,
|
||||
@ -63,7 +71,11 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x: u64 = 618_64_9189_73_511;
|
||||
///
|
||||
/// // Good
|
||||
/// let x: u64 = 61_864_918_973_511;
|
||||
/// ```
|
||||
pub INCONSISTENT_DIGIT_GROUPING,
|
||||
style,
|
||||
|
@ -8,7 +8,7 @@ use crate::utils::{
|
||||
multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help,
|
||||
span_lint_and_sugg, span_lint_and_then, SpanlessEq,
|
||||
};
|
||||
use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sugg};
|
||||
use crate::utils::{is_type_diagnostic_item, qpath_res, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
@ -24,10 +24,10 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, Ty, TyS};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::BytePos;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase};
|
||||
use std::iter::{once, Iterator};
|
||||
use std::mem;
|
||||
@ -1256,7 +1256,7 @@ fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>, e
|
||||
} else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) {
|
||||
let receiver_ty = cx.tables.expr_ty(&args[0]);
|
||||
let receiver_ty_adjusted = cx.tables.expr_ty_adjusted(&args[0]);
|
||||
if same_tys(cx, receiver_ty, receiver_ty_adjusted) {
|
||||
if TyS::same_type(receiver_ty, receiver_ty_adjusted) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
@ -1277,7 +1277,7 @@ fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>, e
|
||||
mutbl: Mutability::Not,
|
||||
},
|
||||
);
|
||||
if same_tys(cx, receiver_ty_adjusted, ref_receiver_ty) {
|
||||
if TyS::same_type(receiver_ty_adjusted, ref_receiver_ty) {
|
||||
lint_iter_method(cx, args, arg, method_name)
|
||||
}
|
||||
}
|
||||
@ -2381,32 +2381,32 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, '
|
||||
match_type(cx, ty, &paths::BTREEMAP) ||
|
||||
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {
|
||||
if method.ident.name == sym!(len) {
|
||||
let span = shorten_needless_collect_span(expr);
|
||||
let span = shorten_span(expr, sym!(collect));
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
span,
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
"replace with",
|
||||
".count()".to_string(),
|
||||
"count()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
if method.ident.name == sym!(is_empty) {
|
||||
let span = shorten_needless_collect_span(expr);
|
||||
let span = shorten_span(expr, sym!(iter));
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
span,
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
"replace with",
|
||||
".next().is_none()".to_string(),
|
||||
"get(0).is_none()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
if method.ident.name == sym!(contains) {
|
||||
let contains_arg = snippet(cx, args[1].span, "??");
|
||||
let span = shorten_needless_collect_span(expr);
|
||||
let span = shorten_span(expr, sym!(collect));
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
@ -2422,7 +2422,7 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, '
|
||||
span,
|
||||
"replace with",
|
||||
format!(
|
||||
".any(|{}| x == {})",
|
||||
"any(|{}| x == {})",
|
||||
arg, pred
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
@ -2435,13 +2435,13 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, '
|
||||
}
|
||||
}
|
||||
|
||||
fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(_, _, ref args) = expr.kind;
|
||||
if let ExprKind::MethodCall(_, ref span, _) = args[0].kind;
|
||||
then {
|
||||
return expr.span.with_lo(span.lo() - BytePos(1));
|
||||
fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span {
|
||||
let mut current_expr = expr;
|
||||
while let ExprKind::MethodCall(ref path, ref span, ref args) = current_expr.kind {
|
||||
if path.ident.name == target_fn_name {
|
||||
return expr.span.with_lo(span.lo());
|
||||
}
|
||||
current_expr = &args[0];
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -36,10 +36,17 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # fn bar(stool: &str) {}
|
||||
/// # let x = Some("abc");
|
||||
///
|
||||
/// // Bad
|
||||
/// match x {
|
||||
/// Some(ref foo) => bar(foo),
|
||||
/// _ => (),
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// if let Some(ref foo) = x {
|
||||
/// bar(foo);
|
||||
/// }
|
||||
/// ```
|
||||
pub SINGLE_MATCH,
|
||||
style,
|
||||
@ -97,11 +104,19 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// match x {
|
||||
/// &A(ref y) => foo(y),
|
||||
/// &B => bar(),
|
||||
/// _ => frob(&x),
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// match *x {
|
||||
/// A(ref y) => foo(y),
|
||||
/// B => bar(),
|
||||
/// _ => frob(x),
|
||||
/// }
|
||||
/// ```
|
||||
pub MATCH_REF_PATS,
|
||||
style,
|
||||
@ -197,10 +212,15 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x: Option<()> = None;
|
||||
///
|
||||
/// // Bad
|
||||
/// let r: Option<&()> = match x {
|
||||
/// None => None,
|
||||
/// Some(ref v) => Some(v),
|
||||
/// };
|
||||
///
|
||||
/// // Good
|
||||
/// let r: Option<&()> = x.as_ref();
|
||||
/// ```
|
||||
pub MATCH_AS_REF,
|
||||
complexity,
|
||||
@ -219,10 +239,18 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # enum Foo { A(usize), B(usize) }
|
||||
/// # let x = Foo::B(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// match x {
|
||||
/// Foo::A(_) => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// match x {
|
||||
/// Foo::A(_) => {},
|
||||
/// Foo::B(_) => {},
|
||||
/// }
|
||||
/// ```
|
||||
pub WILDCARD_ENUM_MATCH_ARM,
|
||||
restriction,
|
||||
@ -242,16 +270,14 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # enum Foo { A, B, C }
|
||||
/// # let x = Foo::B;
|
||||
/// // Bad
|
||||
/// match x {
|
||||
/// Foo::A => {},
|
||||
/// Foo::B => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # enum Foo { A, B, C }
|
||||
/// # let x = Foo::B;
|
||||
///
|
||||
/// // Good
|
||||
/// match x {
|
||||
/// Foo::A => {},
|
||||
/// Foo::B => {},
|
||||
@ -273,10 +299,17 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// match "foo" {
|
||||
/// "a" => {},
|
||||
/// "bar" | _ => {},
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// match "foo" {
|
||||
/// "a" => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
pub WILDCARD_IN_OR_PATTERNS,
|
||||
complexity,
|
||||
|
@ -57,7 +57,7 @@ pub fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<
|
||||
);
|
||||
} else {
|
||||
match (mm, arith) {
|
||||
(MinMax::Max, "add") | (MinMax::Max, "mul") | (MinMax::Min, "sub") => (),
|
||||
(MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (),
|
||||
_ => return,
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, Ty, TyS};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{sym, SymbolStr};
|
||||
@ -26,12 +26,12 @@ use rustc_span::symbol::{sym, SymbolStr};
|
||||
use crate::consts::{constant, Constant};
|
||||
use crate::utils::usage::mutated_variables;
|
||||
use crate::utils::{
|
||||
get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy,
|
||||
get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy,
|
||||
is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
|
||||
match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths,
|
||||
remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_applicability,
|
||||
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
|
||||
span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
|
||||
remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite,
|
||||
span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty,
|
||||
walk_ptrs_ty_depth, SpanlessEq,
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -218,7 +218,12 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let x = Ok::<_, ()>(());
|
||||
/// x.ok().expect("why did I do this again?")
|
||||
///
|
||||
/// // Bad
|
||||
/// x.ok().expect("why did I do this again?");
|
||||
///
|
||||
/// // Good
|
||||
/// x.expect("why did I do this again?");
|
||||
/// ```
|
||||
pub OK_EXPECT,
|
||||
style,
|
||||
@ -273,8 +278,12 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let opt = Some(1);
|
||||
/// opt.map_or(None, |a| Some(a + 1))
|
||||
/// # ;
|
||||
///
|
||||
/// // Bad
|
||||
/// opt.map_or(None, |a| Some(a + 1));
|
||||
///
|
||||
/// // Good
|
||||
/// opt.and_then(|a| Some(a + 1));
|
||||
/// ```
|
||||
pub OPTION_MAP_OR_NONE,
|
||||
style,
|
||||
@ -390,14 +399,19 @@ declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.map(_).flatten(_)`,
|
||||
///
|
||||
/// **Why is this bad?** Readability, this can be written more concisely as a
|
||||
/// single method call.
|
||||
/// single method call using `_.flat_map(_)`
|
||||
///
|
||||
/// **Known problems:**
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let vec = vec![vec![1]];
|
||||
///
|
||||
/// // Bad
|
||||
/// vec.iter().map(|x| x.iter()).flatten();
|
||||
///
|
||||
/// // Good
|
||||
/// vec.iter().flat_map(|x| x.iter());
|
||||
/// ```
|
||||
pub MAP_FLATTEN,
|
||||
pedantic,
|
||||
@ -417,7 +431,16 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let vec = vec![1];
|
||||
///
|
||||
/// // Bad
|
||||
/// vec.iter().filter(|x| **x == 0).map(|x| *x * 2);
|
||||
///
|
||||
/// // Good
|
||||
/// vec.iter().filter_map(|x| if *x == 0 {
|
||||
/// Some(*x * 2)
|
||||
/// } else {
|
||||
/// None
|
||||
/// });
|
||||
/// ```
|
||||
pub FILTER_MAP,
|
||||
pedantic,
|
||||
@ -634,7 +657,12 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # use std::rc::Rc;
|
||||
/// let x = Rc::new(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// x.clone();
|
||||
///
|
||||
/// // Good
|
||||
/// Rc::clone(&x);
|
||||
/// ```
|
||||
pub CLONE_ON_REF_PTR,
|
||||
restriction,
|
||||
@ -741,7 +769,12 @@ declare_clippy_lint! {
|
||||
/// **Known problems:** Does not catch multi-byte unicode characters.
|
||||
///
|
||||
/// **Example:**
|
||||
/// `_.split("x")` could be `_.split('x')`
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// _.split("x");
|
||||
///
|
||||
/// // Good
|
||||
/// _.split('x');
|
||||
pub SINGLE_CHAR_PATTERN,
|
||||
perf,
|
||||
"using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
|
||||
@ -964,8 +997,8 @@ declare_clippy_lint! {
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `.chars().last()` or
|
||||
/// `.chars().next_back()` on a `str` to check if it ends with a given char.
|
||||
/// **What it does:** Checks for usage of `_.chars().last()` or
|
||||
/// `_.chars().next_back()` on a `str` to check if it ends with a given char.
|
||||
///
|
||||
/// **Why is this bad?** Readability, this can be written more concisely as
|
||||
/// `_.ends_with(_)`.
|
||||
@ -975,8 +1008,12 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let name = "_";
|
||||
/// name.chars().last() == Some('_') || name.chars().next_back() == Some('-')
|
||||
/// # ;
|
||||
///
|
||||
/// // Bad
|
||||
/// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
|
||||
///
|
||||
/// // Good
|
||||
/// name.ends_with('_') || name.ends_with('-');
|
||||
/// ```
|
||||
pub CHARS_LAST_CMP,
|
||||
style,
|
||||
@ -1044,17 +1081,15 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
|
||||
/// ```
|
||||
/// As there is no transformation of the argument this could be written as:
|
||||
/// ```rust
|
||||
///
|
||||
/// // As there is no transformation of the argument this could be written as:
|
||||
/// let _ = (0..3).filter(|&x| x > 2);
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// let _ = (0..4).filter_map(|x| Some(x + 1));
|
||||
/// ```
|
||||
/// As there is no conditional check on the argument this could be written as:
|
||||
/// ```rust
|
||||
///
|
||||
/// // As there is no conditional check on the argument this could be written as:
|
||||
/// let _ = (0..4).map(|x| x + 1);
|
||||
/// ```
|
||||
pub UNNECESSARY_FILTER_MAP,
|
||||
@ -1075,7 +1110,11 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let _ = (&vec![3, 4, 5]).into_iter();
|
||||
///
|
||||
/// // Good
|
||||
/// let _ = (&vec![3, 4, 5]).iter();
|
||||
/// ```
|
||||
pub INTO_ITER_ON_REF,
|
||||
style,
|
||||
@ -1242,6 +1281,32 @@ declare_clippy_lint! {
|
||||
"using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `iter().next()` on a Slice or an Array
|
||||
///
|
||||
/// **Why is this bad?** These can be shortened into `.get()`
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let a = [1, 2, 3];
|
||||
/// # let b = vec![1, 2, 3];
|
||||
/// a[2..].iter().next();
|
||||
/// b.iter().next();
|
||||
/// ```
|
||||
/// should be written as:
|
||||
/// ```rust
|
||||
/// # let a = [1, 2, 3];
|
||||
/// # let b = vec![1, 2, 3];
|
||||
/// a.get(2);
|
||||
/// b.get(0);
|
||||
/// ```
|
||||
pub ITER_NEXT_SLICE,
|
||||
style,
|
||||
"using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Methods => [
|
||||
UNWRAP_USED,
|
||||
EXPECT_USED,
|
||||
@ -1273,6 +1338,7 @@ declare_lint_pass!(Methods => [
|
||||
FIND_MAP,
|
||||
MAP_FLATTEN,
|
||||
ITERATOR_STEP_BY_ZERO,
|
||||
ITER_NEXT_SLICE,
|
||||
ITER_NTH,
|
||||
ITER_NTH_ZERO,
|
||||
ITER_SKIP_NEXT,
|
||||
@ -1320,6 +1386,7 @@ impl<'a, 'tcx> LateLintPass<'a, '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_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]),
|
||||
@ -1336,9 +1403,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
|
||||
lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1])
|
||||
},
|
||||
["extend", ..] => lint_extend(cx, expr, arg_lists[0]),
|
||||
["as_ptr", "unwrap"] | ["as_ptr", "expect"] => {
|
||||
lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0])
|
||||
},
|
||||
["as_ptr", "unwrap" | "expect"] => lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0]),
|
||||
["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false),
|
||||
["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true),
|
||||
["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]),
|
||||
@ -1351,12 +1416,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
|
||||
["filter_map", ..] => unnecessary_filter_map::lint(cx, expr, arg_lists[0]),
|
||||
["count", "map"] => lint_suspicious_map(cx, expr),
|
||||
["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr),
|
||||
["unwrap_or", arith @ "checked_add"]
|
||||
| ["unwrap_or", arith @ "checked_sub"]
|
||||
| ["unwrap_or", arith @ "checked_mul"] => {
|
||||
["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => {
|
||||
manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..])
|
||||
},
|
||||
["add"] | ["offset"] | ["sub"] | ["wrapping_offset"] | ["wrapping_add"] | ["wrapping_sub"] => {
|
||||
["add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub"] => {
|
||||
check_pointer_offset(cx, expr, arg_lists[0])
|
||||
},
|
||||
["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
|
||||
@ -1481,7 +1544,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
|
||||
|
||||
let contains_self_ty = |ty: Ty<'tcx>| {
|
||||
ty.walk().any(|inner| match inner.unpack() {
|
||||
GenericArgKind::Type(inner_ty) => same_tys(cx, self_ty, inner_ty),
|
||||
GenericArgKind::Type(inner_ty) => TyS::same_type(self_ty, inner_ty),
|
||||
|
||||
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
|
||||
})
|
||||
@ -1508,7 +1571,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
|
||||
}
|
||||
}
|
||||
|
||||
if name == "new" && !same_tys(cx, ret_ty, self_ty) {
|
||||
if name == "new" && !TyS::same_type(ret_ty, self_ty) {
|
||||
span_lint(
|
||||
cx,
|
||||
NEW_RET_NO_SELF,
|
||||
@ -1762,8 +1825,7 @@ fn lint_expect_fun_call(
|
||||
hir::ExprKind::Call(fun, _) => {
|
||||
if let hir::ExprKind::Path(ref p) = fun.kind {
|
||||
match cx.tables.qpath_res(p, fun.hir_id) {
|
||||
hir::def::Res::Def(hir::def::DefKind::Fn, def_id)
|
||||
| hir::def::Res::Def(hir::def::DefKind::AssocFn, def_id) => matches!(
|
||||
hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!(
|
||||
cx.tcx.fn_sig(def_id).output().skip_binder().kind,
|
||||
ty::Ref(ty::ReStatic, ..)
|
||||
),
|
||||
@ -2199,6 +2261,60 @@ fn lint_step_by<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, args
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_iter_next<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) {
|
||||
let caller_expr = &iter_args[0];
|
||||
|
||||
// Skip lint if the `iter().next()` expression is a for loop argument,
|
||||
// since it is already covered by `&loops::ITER_NEXT_LOOP`
|
||||
let mut parent_expr_opt = get_parent_expr(cx, expr);
|
||||
while let Some(parent_expr) = parent_expr_opt {
|
||||
if higher::for_loop(parent_expr).is_some() {
|
||||
return;
|
||||
}
|
||||
parent_expr_opt = get_parent_expr(cx, parent_expr);
|
||||
}
|
||||
|
||||
if derefs_to_slice(cx, caller_expr, cx.tables.expr_ty(caller_expr)).is_some() {
|
||||
// caller is a Slice
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Index(ref caller_var, ref index_expr) = &caller_expr.kind;
|
||||
if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen })
|
||||
= higher::range(cx, index_expr);
|
||||
if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind;
|
||||
if let ast::LitKind::Int(start_idx, _) = start_lit.node;
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_NEXT_SLICE,
|
||||
expr.span,
|
||||
"Using `.iter().next()` on a Slice without end index.",
|
||||
"try calling",
|
||||
format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if is_type_diagnostic_item(cx, cx.tables.expr_ty(caller_expr), sym!(vec_type))
|
||||
|| matches!(&walk_ptrs_ty(cx.tables.expr_ty(caller_expr)).kind, ty::Array(_, _))
|
||||
{
|
||||
// caller is a Vec or an Array
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_NEXT_SLICE,
|
||||
expr.span,
|
||||
"Using `.iter().next()` on an array",
|
||||
"try calling",
|
||||
format!(
|
||||
"{}.get(0)",
|
||||
snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_iter_nth<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
expr: &hir::Expr<'_>,
|
||||
|
@ -38,10 +38,16 @@ declare_clippy_lint! {
|
||||
/// dereferences, e.g., changing `*x` to `x` within the function.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// fn foo(ref x: u8) -> bool {
|
||||
/// true
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// fn foo(x: &u8) -> bool {
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
pub TOPLEVEL_REF_ARG,
|
||||
style,
|
||||
@ -60,7 +66,11 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # let x = 1.0;
|
||||
///
|
||||
/// // Bad
|
||||
/// if x == f32::NAN { }
|
||||
///
|
||||
/// // Good
|
||||
/// if x.is_nan() { }
|
||||
/// ```
|
||||
pub CMP_NAN,
|
||||
correctness,
|
||||
@ -83,8 +93,15 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// let x = 1.2331f64;
|
||||
/// let y = 1.2332f64;
|
||||
///
|
||||
/// // Bad
|
||||
/// if y == 1.23f64 { }
|
||||
/// if y != x {} // where both are floats
|
||||
///
|
||||
/// // Good
|
||||
/// let error = 0.01f64; // Use an epsilon for comparison
|
||||
/// if (y - 1.23f64).abs() < error { }
|
||||
/// if (y - x).abs() > error { }
|
||||
/// ```
|
||||
pub FLOAT_CMP,
|
||||
correctness,
|
||||
@ -191,7 +208,11 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let a = 0 as *const u32;
|
||||
///
|
||||
/// // Good
|
||||
/// let a = std::ptr::null::<u32>();
|
||||
/// ```
|
||||
pub ZERO_PTR,
|
||||
style,
|
||||
@ -214,7 +235,13 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// let x: f64 = 1.0;
|
||||
/// const ONE: f64 = 1.00;
|
||||
/// x == ONE; // where both are floats
|
||||
///
|
||||
/// // Bad
|
||||
/// if x == ONE { } // where both are floats
|
||||
///
|
||||
/// // Good
|
||||
/// let error = 0.1f64; // Use an epsilon for comparison
|
||||
/// if (x - ONE).abs() < error { }
|
||||
/// ```
|
||||
pub FLOAT_CMP_CONST,
|
||||
restriction,
|
||||
@ -248,17 +275,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MiscLints {
|
||||
return;
|
||||
}
|
||||
for arg in iter_input_pats(decl, body) {
|
||||
match arg.pat.kind {
|
||||
PatKind::Binding(BindingAnnotation::Ref, ..) | PatKind::Binding(BindingAnnotation::RefMut, ..) => {
|
||||
span_lint(
|
||||
cx,
|
||||
TOPLEVEL_REF_ARG,
|
||||
arg.pat.span,
|
||||
"`ref` directly on a function argument is ignored. Consider using a reference type \
|
||||
instead.",
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind {
|
||||
span_lint(
|
||||
cx,
|
||||
TOPLEVEL_REF_ARG,
|
||||
arg.pat.span,
|
||||
"`ref` directly on a function argument is ignored. \
|
||||
Consider using a reference type instead.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,11 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// fn foo(a: i32, _a: i32) {}
|
||||
///
|
||||
/// // Good
|
||||
/// fn bar(a: i32, _b: i32) {}
|
||||
/// ```
|
||||
pub DUPLICATE_UNDERSCORE_ARGUMENT,
|
||||
style,
|
||||
@ -77,7 +81,11 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// (|| 42)()
|
||||
/// // Bad
|
||||
/// let a = (|| 42)()
|
||||
///
|
||||
/// // Good
|
||||
/// let a = 42
|
||||
/// ```
|
||||
pub REDUNDANT_CLOSURE_CALL,
|
||||
complexity,
|
||||
@ -112,7 +120,11 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let y = 0x1a9BAcD;
|
||||
///
|
||||
/// // Good
|
||||
/// let y = 0x1A9BACD;
|
||||
/// ```
|
||||
pub MIXED_CASE_HEX_LITERALS,
|
||||
style,
|
||||
@ -129,7 +141,11 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let y = 123832i32;
|
||||
///
|
||||
/// // Good
|
||||
/// let y = 123832_i32;
|
||||
/// ```
|
||||
pub UNSEPARATED_LITERAL_SUFFIX,
|
||||
pedantic,
|
||||
@ -207,9 +223,16 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # let v = Some("abc");
|
||||
///
|
||||
/// // Bad
|
||||
/// match v {
|
||||
/// Some(x) => (),
|
||||
/// y @ _ => (), // easier written as `y`,
|
||||
/// y @ _ => (),
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// match v {
|
||||
/// Some(x) => (),
|
||||
/// y => (),
|
||||
/// }
|
||||
/// ```
|
||||
pub REDUNDANT_PATTERN,
|
||||
@ -235,16 +258,13 @@ declare_clippy_lint! {
|
||||
/// # struct TupleStruct(u32, u32, u32);
|
||||
/// # let t = TupleStruct(1, 2, 3);
|
||||
///
|
||||
/// // Bad
|
||||
/// match t {
|
||||
/// TupleStruct(0, .., _) => (),
|
||||
/// _ => (),
|
||||
/// }
|
||||
/// ```
|
||||
/// can be written as
|
||||
/// ```rust
|
||||
/// # struct TupleStruct(u32, u32, u32);
|
||||
/// # let t = TupleStruct(1, 2, 3);
|
||||
///
|
||||
/// // Good
|
||||
/// match t {
|
||||
/// TupleStruct(0, ..) => (),
|
||||
/// _ => (),
|
||||
|
@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::DUMMY_SP;
|
||||
|
||||
use cargo_metadata::{DependencyKind, MetadataCommand, Node, Package, PackageId};
|
||||
use cargo_metadata::{DependencyKind, Node, Package, PackageId};
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
|
||||
@ -42,13 +42,7 @@ impl LateLintPass<'_, '_> for MultipleCrateVersions {
|
||||
return;
|
||||
}
|
||||
|
||||
let metadata = if let Ok(metadata) = MetadataCommand::new().exec() {
|
||||
metadata
|
||||
} else {
|
||||
span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata");
|
||||
return;
|
||||
};
|
||||
|
||||
let metadata = unwrap_cargo_metadata!(cx, MULTIPLE_CRATE_VERSIONS, true);
|
||||
let local_name = cx.tcx.crate_name(LOCAL_CRATE).as_str();
|
||||
let mut packages = metadata.packages;
|
||||
packages.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
|
@ -16,7 +16,11 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// // Bad
|
||||
/// my_vec.push(&mut value)
|
||||
///
|
||||
/// // Good
|
||||
/// my_vec.push(&value)
|
||||
/// ```
|
||||
pub UNNECESSARY_MUT_PASSED,
|
||||
style,
|
||||
|
@ -22,9 +22,15 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let y = true;
|
||||
///
|
||||
/// // Bad
|
||||
/// # use std::sync::Mutex;
|
||||
/// # let y = 1;
|
||||
/// let x = Mutex::new(&y);
|
||||
///
|
||||
/// // Good
|
||||
/// # use std::sync::atomic::AtomicBool;
|
||||
/// let x = AtomicBool::new(y);
|
||||
/// ```
|
||||
pub MUTEX_ATOMIC,
|
||||
perf,
|
||||
@ -46,6 +52,10 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # use std::sync::Mutex;
|
||||
/// let x = Mutex::new(0usize);
|
||||
///
|
||||
/// // Good
|
||||
/// # use std::sync::atomic::AtomicUsize;
|
||||
/// let x = AtomicUsize::new(0usize);
|
||||
/// ```
|
||||
pub MUTEX_INTEGER,
|
||||
nursery,
|
||||
|
@ -15,8 +15,7 @@ use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for expressions of the form `if c { true } else {
|
||||
/// false }`
|
||||
/// (or vice versa) and suggest using the condition directly.
|
||||
/// false }` (or vice versa) and suggests using the condition directly.
|
||||
///
|
||||
/// **Why is this bad?** Redundant code.
|
||||
///
|
||||
|
@ -18,12 +18,16 @@ declare_clippy_lint! {
|
||||
/// **Why is this bad?** Suggests that the receiver of the expression borrows
|
||||
/// the expression.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x: &i32 = &&&&&&5;
|
||||
/// ```
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
/// // Good
|
||||
/// let x: &i32 = &5;
|
||||
/// ```
|
||||
pub NEEDLESS_BORROW,
|
||||
nursery,
|
||||
"taking a reference that is going to be automatically dereferenced"
|
||||
|
@ -424,7 +424,7 @@ fn erode_from_back(s: &str) -> String {
|
||||
}
|
||||
|
||||
fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> {
|
||||
block.stmts.iter().next().map(|stmt| stmt.span)
|
||||
block.stmts.get(0).map(|stmt| stmt.span)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -21,6 +21,16 @@ declare_clippy_lint! {
|
||||
/// # z: i32,
|
||||
/// # }
|
||||
/// # let zero_point = Point { x: 0, y: 0, z: 0 };
|
||||
///
|
||||
/// // Bad
|
||||
/// Point {
|
||||
/// x: 1,
|
||||
/// y: 1,
|
||||
/// z: 1,
|
||||
/// ..zero_point
|
||||
/// };
|
||||
///
|
||||
/// // Ok
|
||||
/// Point {
|
||||
/// x: 1,
|
||||
/// y: 1,
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::utils::paths;
|
||||
use crate::utils::sugg::DiagnosticBuilderExt;
|
||||
use crate::utils::{get_trait_def_id, return_ty, same_tys, span_lint_hir_and_then};
|
||||
use crate::utils::{get_trait_def_id, return_ty, span_lint_hir_and_then};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::HirIdSet;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::{Ty, TyS};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -93,7 +93,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault {
|
||||
let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
|
||||
let self_ty = cx.tcx.type_of(self_def_id);
|
||||
if_chain! {
|
||||
if same_tys(cx, self_ty, return_ty(cx, id));
|
||||
if TyS::same_type(self_ty, return_ty(cx, id));
|
||||
if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
|
||||
then {
|
||||
if self.impling_types.is_none() {
|
||||
|
@ -147,7 +147,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option
|
||||
if let ExprKind::Path(ref qpath) = callee.kind {
|
||||
let res = qpath_res(cx, qpath, callee.hir_id);
|
||||
match res {
|
||||
Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(..), _)
|
||||
Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
|
||||
if !has_drop(cx, cx.tables.expr_ty(expr)) =>
|
||||
{
|
||||
Some(args.iter().collect())
|
||||
|
@ -47,7 +47,11 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// // Bad
|
||||
/// fn foo(&Vec<u32>) { .. }
|
||||
///
|
||||
/// // Good
|
||||
/// fn foo(&[u32]) { .. }
|
||||
/// ```
|
||||
pub PTR_ARG,
|
||||
style,
|
||||
@ -65,9 +69,15 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// // Bad
|
||||
/// if x == ptr::null {
|
||||
/// ..
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// if x.is_null() {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
pub CMP_NULL,
|
||||
style,
|
||||
@ -76,19 +86,16 @@ declare_clippy_lint! {
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** This lint checks for functions that take immutable
|
||||
/// references and return
|
||||
/// mutable ones.
|
||||
/// references and return mutable ones.
|
||||
///
|
||||
/// **Why is this bad?** This is trivially unsound, as one can create two
|
||||
/// mutable references
|
||||
/// from the same (immutable!) source. This
|
||||
/// [error](https://github.com/rust-lang/rust/issues/39465)
|
||||
/// mutable references from the same (immutable!) source.
|
||||
/// This [error](https://github.com/rust-lang/rust/issues/39465)
|
||||
/// actually lead to an interim Rust release 1.15.1.
|
||||
///
|
||||
/// **Known problems:** To be on the conservative side, if there's at least one
|
||||
/// mutable reference
|
||||
/// with the output lifetime, this lint will not trigger. In practice, this
|
||||
/// case is unlikely anyway.
|
||||
/// mutable reference with the output lifetime, this lint will not trigger.
|
||||
/// In practice, this case is unlikely anyway.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
|
@ -88,7 +88,7 @@ impl QuestionMark {
|
||||
replacement_str,
|
||||
applicability,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -241,14 +241,26 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
|
||||
}
|
||||
|
||||
fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
|
||||
fn inside_indexing_expr<'a>(cx: &'a LateContext<'_, '_>, expr: &Expr<'_>) -> Option<&'a Expr<'a>> {
|
||||
match get_parent_expr(cx, expr) {
|
||||
parent_expr @ Some(Expr {
|
||||
fn inside_indexing_expr(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
|
||||
matches!(
|
||||
get_parent_expr(cx, expr),
|
||||
Some(Expr {
|
||||
kind: ExprKind::Index(..),
|
||||
..
|
||||
}) => parent_expr,
|
||||
_ => None,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fn is_for_loop_arg(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
|
||||
let mut cur_expr = expr;
|
||||
while let Some(parent_expr) = get_parent_expr(cx, cur_expr) {
|
||||
match higher::for_loop(parent_expr) {
|
||||
Some((_, args, _)) if args.hir_id == expr.hir_id => return true,
|
||||
_ => cur_expr = parent_expr,
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool {
|
||||
@ -267,34 +279,18 @@ fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
|
||||
if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx);
|
||||
if is_empty_range(limits, ordering);
|
||||
then {
|
||||
if let Some(parent_expr) = inside_indexing_expr(cx, expr) {
|
||||
let (reason, outcome) = if ordering == Ordering::Equal {
|
||||
("empty", "always yield an empty slice")
|
||||
} else {
|
||||
("reversed", "panic at run-time")
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REVERSED_EMPTY_RANGES,
|
||||
expr.span,
|
||||
&format!("this range is {} and using it to index a slice will {}", reason, outcome),
|
||||
|diag| {
|
||||
if_chain! {
|
||||
if ordering == Ordering::Equal;
|
||||
if let ty::Slice(slice_ty) = cx.tables.expr_ty(parent_expr).kind;
|
||||
then {
|
||||
diag.span_suggestion(
|
||||
parent_expr.span,
|
||||
"if you want an empty slice, use",
|
||||
format!("[] as &[{}]", slice_ty),
|
||||
Applicability::MaybeIncorrect
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
if inside_indexing_expr(cx, expr) {
|
||||
// Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ...
|
||||
if ordering != Ordering::Equal {
|
||||
span_lint(
|
||||
cx,
|
||||
REVERSED_EMPTY_RANGES,
|
||||
expr.span,
|
||||
"this range is reversed and using it to index a slice will panic at run-time",
|
||||
);
|
||||
}
|
||||
// ... except in for loop arguments for backwards compatibility with `reverse_range_loop`
|
||||
} else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REVERSED_EMPTY_RANGES,
|
||||
|
@ -16,8 +16,13 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// let a = f(*&mut b);
|
||||
/// let c = *&d;
|
||||
///
|
||||
/// // Good
|
||||
/// let a = f(b);
|
||||
/// let c = d;
|
||||
/// ```
|
||||
pub DEREF_ADDROF,
|
||||
complexity,
|
||||
|
@ -86,11 +86,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Regex {
|
||||
if let Some(span) = is_expn_of(expr.span, "regex");
|
||||
then {
|
||||
if !self.spans.contains(&span) {
|
||||
span_lint(cx,
|
||||
REGEX_MACRO,
|
||||
span,
|
||||
"`regex!(_)` found. \
|
||||
Please use `Regex::new(_)`, which is faster for now.");
|
||||
span_lint(
|
||||
cx,
|
||||
REGEX_MACRO,
|
||||
span,
|
||||
"`regex!(_)` found. \
|
||||
Please use `Regex::new(_)`, which is faster for now."
|
||||
);
|
||||
self.spans.insert(span);
|
||||
}
|
||||
self.last = Some(block.hir_id);
|
||||
|
@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::BytePos;
|
||||
|
||||
use crate::utils::{in_macro, match_path_ast, snippet_opt, span_lint_and_sugg, span_lint_and_then};
|
||||
use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for return statements at the end of a block.
|
||||
@ -36,33 +36,6 @@ declare_clippy_lint! {
|
||||
"using a return statement like `return expr;` where an expression would suffice"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `let`-bindings, which are subsequently
|
||||
/// returned.
|
||||
///
|
||||
/// **Why is this bad?** It is just extraneous code. Remove it to make your code
|
||||
/// more rusty.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo() -> String {
|
||||
/// let x = String::new();
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
/// instead, use
|
||||
/// ```
|
||||
/// fn foo() -> String {
|
||||
/// String::new()
|
||||
/// }
|
||||
/// ```
|
||||
pub LET_AND_RETURN,
|
||||
style,
|
||||
"creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for unit (`()`) expressions that can be removed.
|
||||
///
|
||||
@ -90,7 +63,7 @@ enum RetReplacement {
|
||||
Block,
|
||||
}
|
||||
|
||||
declare_lint_pass!(Return => [NEEDLESS_RETURN, LET_AND_RETURN, UNUSED_UNIT]);
|
||||
declare_lint_pass!(Return => [NEEDLESS_RETURN, UNUSED_UNIT]);
|
||||
|
||||
impl Return {
|
||||
// Check the final stmt or expr in a block for unnecessary return.
|
||||
@ -105,7 +78,7 @@ impl Return {
|
||||
}
|
||||
}
|
||||
|
||||
// Check a the final expression in a block if it's a return.
|
||||
// Check the final expression in a block if it's a return.
|
||||
fn check_final_expr(
|
||||
&mut self,
|
||||
cx: &EarlyContext<'_>,
|
||||
@ -186,54 +159,6 @@ impl Return {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Check for "let x = EXPR; x"
|
||||
fn check_let_return(cx: &EarlyContext<'_>, block: &ast::Block) {
|
||||
let mut it = block.stmts.iter();
|
||||
|
||||
// we need both a let-binding stmt and an expr
|
||||
if_chain! {
|
||||
if let Some(retexpr) = it.next_back();
|
||||
if let ast::StmtKind::Expr(ref retexpr) = retexpr.kind;
|
||||
if let Some(stmt) = it.next_back();
|
||||
if let ast::StmtKind::Local(ref local) = stmt.kind;
|
||||
// don't lint in the presence of type inference
|
||||
if local.ty.is_none();
|
||||
if local.attrs.is_empty();
|
||||
if let Some(ref initexpr) = local.init;
|
||||
if let ast::PatKind::Ident(_, ident, _) = local.pat.kind;
|
||||
if let ast::ExprKind::Path(_, ref path) = retexpr.kind;
|
||||
if match_path_ast(path, &[&*ident.name.as_str()]);
|
||||
if !in_external_macro(cx.sess(), initexpr.span);
|
||||
if !in_external_macro(cx.sess(), retexpr.span);
|
||||
if !in_external_macro(cx.sess(), local.span);
|
||||
if !in_macro(local.span);
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
LET_AND_RETURN,
|
||||
retexpr.span,
|
||||
"returning the result of a `let` binding from a block",
|
||||
|err| {
|
||||
err.span_label(local.span, "unnecessary `let` binding");
|
||||
|
||||
if let Some(snippet) = snippet_opt(cx, initexpr.span) {
|
||||
err.multipart_suggestion(
|
||||
"return the expression directly",
|
||||
vec![
|
||||
(local.span, String::new()),
|
||||
(retexpr.span, snippet),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.span_help(initexpr.span, "this expression can be directly returned");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for Return {
|
||||
@ -254,7 +179,6 @@ impl EarlyLintPass for Return {
|
||||
}
|
||||
|
||||
fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
|
||||
Self::check_let_return(cx, block);
|
||||
if_chain! {
|
||||
if let Some(ref stmt) = block.stmts.last();
|
||||
if let ast::StmtKind::Expr(ref expr) = stmt.kind;
|
||||
|
@ -25,7 +25,12 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
///
|
||||
/// // Bad
|
||||
/// let x = &x;
|
||||
///
|
||||
/// // Good
|
||||
/// let y = &x; // use different variable name
|
||||
/// ```
|
||||
pub SHADOW_SAME,
|
||||
restriction,
|
||||
@ -77,7 +82,12 @@ declare_clippy_lint! {
|
||||
/// # let y = 1;
|
||||
/// # let z = 2;
|
||||
/// let x = y;
|
||||
///
|
||||
/// // Bad
|
||||
/// let x = z; // shadows the earlier binding
|
||||
///
|
||||
/// // Good
|
||||
/// let w = z; // use different variable name
|
||||
/// ```
|
||||
pub SHADOW_UNRELATED,
|
||||
pedantic,
|
||||
|
@ -16,7 +16,7 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ```rust,ignore
|
||||
/// use regex;
|
||||
///
|
||||
/// fn main() {
|
||||
@ -24,7 +24,7 @@ declare_clippy_lint! {
|
||||
/// }
|
||||
/// ```
|
||||
/// Better as
|
||||
/// ```rust, ignore
|
||||
/// ```rust,ignore
|
||||
/// fn main() {
|
||||
/// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
|
||||
/// }
|
||||
|
@ -22,11 +22,17 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # use core::iter::repeat;
|
||||
/// # let len = 4;
|
||||
///
|
||||
/// // Bad
|
||||
/// let mut vec1 = Vec::with_capacity(len);
|
||||
/// vec1.resize(len, 0);
|
||||
///
|
||||
/// let mut vec2 = Vec::with_capacity(len);
|
||||
/// vec2.extend(repeat(0).take(len))
|
||||
/// vec2.extend(repeat(0).take(len));
|
||||
///
|
||||
/// // Good
|
||||
/// let mut vec1 = vec![0; len];
|
||||
/// let mut vec2 = vec![0; len];
|
||||
/// ```
|
||||
pub SLOW_VECTOR_INITIALIZATION,
|
||||
perf,
|
||||
|
@ -24,6 +24,10 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// let mut x = "Hello".to_owned();
|
||||
/// x = x + ", World";
|
||||
///
|
||||
/// // More readable
|
||||
/// x += ", World";
|
||||
/// x.push_str(", World");
|
||||
/// ```
|
||||
pub STRING_ADD_ASSIGN,
|
||||
pedantic,
|
||||
@ -69,7 +73,11 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let bs = "a byte string".as_bytes();
|
||||
///
|
||||
/// // Good
|
||||
/// let bs = b"a byte string";
|
||||
/// ```
|
||||
pub STRING_LIT_AS_BYTES,
|
||||
style,
|
||||
|
@ -71,8 +71,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for SuspiciousImpl {
|
||||
if let hir::Node::Expr(e) = cx.tcx.hir().get(parent_expr) {
|
||||
match e.kind {
|
||||
hir::ExprKind::Binary(..)
|
||||
| hir::ExprKind::Unary(hir::UnOp::UnNot, _)
|
||||
| hir::ExprKind::Unary(hir::UnOp::UnNeg, _)
|
||||
| hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _)
|
||||
| hir::ExprKind::AssignOp(..) => return,
|
||||
_ => {},
|
||||
}
|
||||
@ -191,8 +190,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BinaryExprVisitor {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
match expr.kind {
|
||||
hir::ExprKind::Binary(..)
|
||||
| hir::ExprKind::Unary(hir::UnOp::UnNot, _)
|
||||
| hir::ExprKind::Unary(hir::UnOp::UnNeg, _)
|
||||
| hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _)
|
||||
| hir::ExprKind::AssignOp(..) => self.in_binary_expr = true,
|
||||
_ => {},
|
||||
}
|
||||
|
@ -312,7 +312,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
e.span,
|
||||
&format!("transmute from a type (`{}`) to itself", from_ty),
|
||||
),
|
||||
(&ty::Ref(_, rty, rty_mutbl), &ty::RawPtr(ptr_ty)) => span_lint_and_then(
|
||||
(ty::Ref(_, rty, rty_mutbl), ty::RawPtr(ptr_ty)) => span_lint_and_then(
|
||||
cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
@ -321,10 +321,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
||||
let rty_and_mut = ty::TypeAndMut {
|
||||
ty: rty,
|
||||
mutbl: rty_mutbl,
|
||||
mutbl: *rty_mutbl,
|
||||
};
|
||||
|
||||
let sugg = if ptr_ty == rty_and_mut {
|
||||
let sugg = if *ptr_ty == rty_and_mut {
|
||||
arg.as_ty(to_ty)
|
||||
} else {
|
||||
arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty)
|
||||
@ -334,7 +334,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
}
|
||||
},
|
||||
),
|
||||
(&ty::Int(_), &ty::RawPtr(_)) | (&ty::Uint(_), &ty::RawPtr(_)) => span_lint_and_then(
|
||||
(ty::Int(_) | ty::Uint(_), ty::RawPtr(_)) => span_lint_and_then(
|
||||
cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
@ -350,16 +350,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
}
|
||||
},
|
||||
),
|
||||
(&ty::Float(_), &ty::Ref(..))
|
||||
| (&ty::Float(_), &ty::RawPtr(_))
|
||||
| (&ty::Char, &ty::Ref(..))
|
||||
| (&ty::Char, &ty::RawPtr(_)) => span_lint(
|
||||
(ty::Float(_) | ty::Char, ty::Ref(..) | ty::RawPtr(_)) => span_lint(
|
||||
cx,
|
||||
WRONG_TRANSMUTE,
|
||||
e.span,
|
||||
&format!("transmute from a `{}` to a pointer", from_ty),
|
||||
),
|
||||
(&ty::RawPtr(from_ptr), _) if from_ptr.ty == to_ty => span_lint(
|
||||
(ty::RawPtr(from_ptr), _) if from_ptr.ty == to_ty => span_lint(
|
||||
cx,
|
||||
CROSSPOINTER_TRANSMUTE,
|
||||
e.span,
|
||||
@ -368,7 +365,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
from_ty, to_ty
|
||||
),
|
||||
),
|
||||
(_, &ty::RawPtr(to_ptr)) if to_ptr.ty == from_ty => span_lint(
|
||||
(_, ty::RawPtr(to_ptr)) if to_ptr.ty == from_ty => span_lint(
|
||||
cx,
|
||||
CROSSPOINTER_TRANSMUTE,
|
||||
e.span,
|
||||
@ -377,7 +374,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
from_ty, to_ty
|
||||
),
|
||||
),
|
||||
(&ty::RawPtr(from_pty), &ty::Ref(_, to_ref_ty, mutbl)) => span_lint_and_then(
|
||||
(ty::RawPtr(from_pty), ty::Ref(_, to_ref_ty, mutbl)) => span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_PTR_TO_REF,
|
||||
e.span,
|
||||
@ -388,13 +385,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
),
|
||||
|diag| {
|
||||
let arg = sugg::Sugg::hir(cx, &args[0], "..");
|
||||
let (deref, cast) = if mutbl == Mutability::Mut {
|
||||
let (deref, cast) = if *mutbl == Mutability::Mut {
|
||||
("&mut *", "*mut")
|
||||
} else {
|
||||
("&*", "*const")
|
||||
};
|
||||
|
||||
let arg = if from_pty.ty == to_ref_ty {
|
||||
let arg = if from_pty.ty == *to_ref_ty {
|
||||
arg
|
||||
} else {
|
||||
arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_ref_ty)))
|
||||
@ -408,7 +405,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
);
|
||||
},
|
||||
),
|
||||
(&ty::Int(ast::IntTy::I32), &ty::Char) | (&ty::Uint(ast::UintTy::U32), &ty::Char) => {
|
||||
(ty::Int(ast::IntTy::I32) | ty::Uint(ast::UintTy::U32), &ty::Char) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_INT_TO_CHAR,
|
||||
@ -430,13 +427,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
},
|
||||
)
|
||||
},
|
||||
(&ty::Ref(_, ty_from, from_mutbl), &ty::Ref(_, ty_to, to_mutbl)) => {
|
||||
(ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) => {
|
||||
if_chain! {
|
||||
if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind, &ty_to.kind);
|
||||
if let ty::Uint(ast::UintTy::U8) = slice_ty.kind;
|
||||
if from_mutbl == to_mutbl;
|
||||
then {
|
||||
let postfix = if from_mutbl == Mutability::Mut {
|
||||
let postfix = if *from_mutbl == Mutability::Mut {
|
||||
"_mut"
|
||||
} else {
|
||||
""
|
||||
@ -465,13 +462,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
|diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
||||
let ty_from_and_mut = ty::TypeAndMut {
|
||||
ty: ty_from,
|
||||
mutbl: from_mutbl
|
||||
mutbl: *from_mutbl
|
||||
};
|
||||
let ty_to_and_mut = ty::TypeAndMut { ty: ty_to, mutbl: to_mutbl };
|
||||
let ty_to_and_mut = ty::TypeAndMut { ty: ty_to, mutbl: *to_mutbl };
|
||||
let sugg_paren = arg
|
||||
.as_ty(cx.tcx.mk_ptr(ty_from_and_mut))
|
||||
.as_ty(cx.tcx.mk_ptr(ty_to_and_mut));
|
||||
let sugg = if to_mutbl == Mutability::Mut {
|
||||
let sugg = if *to_mutbl == Mutability::Mut {
|
||||
sugg_paren.mut_addr_deref()
|
||||
} else {
|
||||
sugg_paren.addr_deref()
|
||||
@ -488,19 +485,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
}
|
||||
}
|
||||
},
|
||||
(&ty::RawPtr(_), &ty::RawPtr(to_ty)) => span_lint_and_then(
|
||||
(ty::RawPtr(_), ty::RawPtr(to_ty)) => span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_PTR_TO_PTR,
|
||||
e.span,
|
||||
"transmute from a pointer to a pointer",
|
||||
|diag| {
|
||||
if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
||||
let sugg = arg.as_ty(cx.tcx.mk_ptr(to_ty));
|
||||
let sugg = arg.as_ty(cx.tcx.mk_ptr(*to_ty));
|
||||
diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified);
|
||||
}
|
||||
},
|
||||
),
|
||||
(&ty::Int(ast::IntTy::I8), &ty::Bool) | (&ty::Uint(ast::UintTy::U8), &ty::Bool) => {
|
||||
(ty::Int(ast::IntTy::I8) | ty::Uint(ast::UintTy::U8), ty::Bool) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_INT_TO_BOOL,
|
||||
@ -518,7 +515,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
},
|
||||
)
|
||||
},
|
||||
(&ty::Int(_), &ty::Float(_)) | (&ty::Uint(_), &ty::Float(_)) => span_lint_and_then(
|
||||
(ty::Int(_) | ty::Uint(_), ty::Float(_)) => span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_INT_TO_FLOAT,
|
||||
e.span,
|
||||
@ -541,7 +538,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
);
|
||||
},
|
||||
),
|
||||
(&ty::Float(float_ty), &ty::Int(_)) | (&ty::Float(float_ty), &ty::Uint(_)) => span_lint_and_then(
|
||||
(ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) => span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_FLOAT_TO_INT,
|
||||
e.span,
|
||||
@ -585,7 +582,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
);
|
||||
},
|
||||
),
|
||||
(&ty::Adt(ref from_adt, ref from_substs), &ty::Adt(ref to_adt, ref to_substs)) => {
|
||||
(ty::Adt(from_adt, from_substs), ty::Adt(to_adt, to_substs)) => {
|
||||
if from_adt.did != to_adt.did ||
|
||||
!COLLECTIONS.iter().any(|path| match_def_path(cx, to_adt.did, path)) {
|
||||
return;
|
||||
|
@ -10,14 +10,14 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{
|
||||
BinOpKind, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
|
||||
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
|
||||
ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn,
|
||||
TraitItem, TraitItemKind, TyKind, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TypeckTables};
|
||||
use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeckTables};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::Span;
|
||||
@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty;
|
||||
use crate::consts::{constant, Constant};
|
||||
use crate::utils::paths;
|
||||
use crate::utils::{
|
||||
clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item,
|
||||
clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item,
|
||||
last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral,
|
||||
qpath_res, same_tys, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite,
|
||||
span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
|
||||
qpath_res, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability,
|
||||
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -779,24 +779,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg {
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args) => {
|
||||
for arg in args {
|
||||
if is_unit(cx.tables.expr_ty(arg)) && !is_unit_literal(arg) {
|
||||
if let ExprKind::Match(.., match_source) = &arg.kind {
|
||||
if *match_source == MatchSource::TryDesugar {
|
||||
continue;
|
||||
let args_to_recover = args
|
||||
.iter()
|
||||
.filter(|arg| {
|
||||
if is_unit(cx.tables.expr_ty(arg)) && !is_unit_literal(arg) {
|
||||
if let ExprKind::Match(.., MatchSource::TryDesugar) = &arg.kind {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNIT_ARG,
|
||||
arg.span,
|
||||
"passing a unit value to a function",
|
||||
"if you intended to pass a unit value, use a unit literal instead",
|
||||
"()".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if !args_to_recover.is_empty() {
|
||||
lint_unit_args(cx, expr, &args_to_recover);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
@ -804,6 +802,101 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg {
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_unit_args(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let (singular, plural) = if args_to_recover.len() > 1 {
|
||||
("", "s")
|
||||
} else {
|
||||
("a ", "")
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNIT_ARG,
|
||||
expr.span,
|
||||
&format!("passing {}unit value{} to a function", singular, plural),
|
||||
|db| {
|
||||
let mut or = "";
|
||||
args_to_recover
|
||||
.iter()
|
||||
.filter_map(|arg| {
|
||||
if_chain! {
|
||||
if let ExprKind::Block(block, _) = arg.kind;
|
||||
if block.expr.is_none();
|
||||
if let Some(last_stmt) = block.stmts.iter().last();
|
||||
if let StmtKind::Semi(last_expr) = last_stmt.kind;
|
||||
if let Some(snip) = snippet_opt(cx, last_expr.span);
|
||||
then {
|
||||
Some((
|
||||
last_stmt.span,
|
||||
snip,
|
||||
))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.for_each(|(span, sugg)| {
|
||||
db.span_suggestion(
|
||||
span,
|
||||
"remove the semicolon from the last statement in the block",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
or = "or ";
|
||||
});
|
||||
let sugg = args_to_recover
|
||||
.iter()
|
||||
.filter(|arg| !is_empty_block(arg))
|
||||
.enumerate()
|
||||
.map(|(i, arg)| {
|
||||
let indent = if i == 0 {
|
||||
0
|
||||
} else {
|
||||
indent_of(cx, expr.span).unwrap_or(0)
|
||||
};
|
||||
format!(
|
||||
"{}{};",
|
||||
" ".repeat(indent),
|
||||
snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
let mut and = "";
|
||||
if !sugg.is_empty() {
|
||||
let plural = if sugg.len() > 1 { "s" } else { "" };
|
||||
db.span_suggestion(
|
||||
expr.span.with_hi(expr.span.lo()),
|
||||
&format!("{}move the expression{} in front of the call...", or, plural),
|
||||
format!("{}\n", sugg.join("\n")),
|
||||
applicability,
|
||||
);
|
||||
and = "...and "
|
||||
}
|
||||
db.multipart_suggestion(
|
||||
&format!("{}use {}unit literal{} instead", and, singular, plural),
|
||||
args_to_recover
|
||||
.iter()
|
||||
.map(|arg| (arg.span, "()".to_string()))
|
||||
.collect::<Vec<_>>(),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn is_empty_block(expr: &Expr<'_>) -> bool {
|
||||
matches!(
|
||||
expr.kind,
|
||||
ExprKind::Block(
|
||||
Block {
|
||||
stmts: &[], expr: None, ..
|
||||
},
|
||||
_,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool {
|
||||
use rustc_span::hygiene::DesugaringKind;
|
||||
if let ExprKind::Call(ref callee, _) = expr.kind {
|
||||
@ -974,7 +1067,8 @@ declare_clippy_lint! {
|
||||
/// behavior.
|
||||
///
|
||||
/// **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
|
||||
/// on the resulting pointer is fine.
|
||||
/// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
|
||||
/// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
@ -982,7 +1076,7 @@ declare_clippy_lint! {
|
||||
/// let _ = (&mut 1u8 as *mut u8) as *mut u16;
|
||||
/// ```
|
||||
pub CAST_PTR_ALIGNMENT,
|
||||
correctness,
|
||||
pedantic,
|
||||
"cast from a pointer to a more-strictly-aligned pointer"
|
||||
}
|
||||
|
||||
@ -2462,7 +2556,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
|
||||
if let ExprKind::Path(QPath::TypeRelative(ref ty, ref method)) = fun.kind;
|
||||
if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
|
||||
then {
|
||||
if !same_tys(self.cx, self.target.ty(), self.body.expr_ty(e)) {
|
||||
if !TyS::same_type(self.target.ty(), self.body.expr_ty(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
267
src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
Normal file
267
src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
Normal file
@ -0,0 +1,267 @@
|
||||
use crate::utils;
|
||||
use crate::utils::paths;
|
||||
use crate::utils::sugg::Sugg;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::Ident;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// Detects when people use `Vec::sort_by` and pass in a function
|
||||
/// which compares the two arguments, either directly or indirectly.
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
|
||||
/// possible) than to use `Vec::sort_by` and and a more complicated
|
||||
/// closure.
|
||||
///
|
||||
/// **Known problems:**
|
||||
/// If the suggested `Vec::sort_by_key` uses Reverse and it isn't
|
||||
/// imported by a use statement in the current frame, then a `use`
|
||||
/// statement that imports it will need to be added (which this lint
|
||||
/// can't do).
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// # struct A;
|
||||
/// # impl A { fn foo(&self) {} }
|
||||
/// # let mut vec: Vec<A> = Vec::new();
|
||||
/// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # struct A;
|
||||
/// # impl A { fn foo(&self) {} }
|
||||
/// # let mut vec: Vec<A> = Vec::new();
|
||||
/// vec.sort_by_key(|a| a.foo());
|
||||
/// ```
|
||||
pub UNNECESSARY_SORT_BY,
|
||||
complexity,
|
||||
"Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]);
|
||||
|
||||
enum LintTrigger {
|
||||
Sort(SortDetection),
|
||||
SortByKey(SortByKeyDetection),
|
||||
}
|
||||
|
||||
struct SortDetection {
|
||||
vec_name: String,
|
||||
unstable: bool,
|
||||
}
|
||||
|
||||
struct SortByKeyDetection {
|
||||
vec_name: String,
|
||||
closure_arg: String,
|
||||
closure_body: String,
|
||||
reverse: bool,
|
||||
unstable: bool,
|
||||
}
|
||||
|
||||
/// Detect if the two expressions are mirrored (identical, except one
|
||||
/// contains a and the other replaces it with b)
|
||||
fn mirrored_exprs(
|
||||
cx: &LateContext<'_, '_>,
|
||||
a_expr: &Expr<'_>,
|
||||
a_ident: &Ident,
|
||||
b_expr: &Expr<'_>,
|
||||
b_ident: &Ident,
|
||||
) -> bool {
|
||||
match (&a_expr.kind, &b_expr.kind) {
|
||||
// Two boxes with mirrored contents
|
||||
(ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
|
||||
mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident)
|
||||
},
|
||||
// Two arrays with mirrored contents
|
||||
(ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => left_exprs
|
||||
.iter()
|
||||
.zip(right_exprs.iter())
|
||||
.all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)),
|
||||
// The two exprs are function calls.
|
||||
// Check to see that the function itself and its arguments are mirrored
|
||||
(ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
|
||||
mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident)
|
||||
&& left_args
|
||||
.iter()
|
||||
.zip(right_args.iter())
|
||||
.all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident))
|
||||
},
|
||||
// The two exprs are method calls.
|
||||
// Check to see that the function is the same and the arguments are mirrored
|
||||
// This is enough because the receiver of the method is listed in the arguments
|
||||
(ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) => {
|
||||
left_segment.ident == right_segment.ident
|
||||
&& left_args
|
||||
.iter()
|
||||
.zip(right_args.iter())
|
||||
.all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident))
|
||||
},
|
||||
// Two tuples with mirrored contents
|
||||
(ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => left_exprs
|
||||
.iter()
|
||||
.zip(right_exprs.iter())
|
||||
.all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)),
|
||||
// Two binary ops, which are the same operation and which have mirrored arguments
|
||||
(ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
|
||||
left_op.node == right_op.node
|
||||
&& mirrored_exprs(cx, left_left, a_ident, right_left, b_ident)
|
||||
&& mirrored_exprs(cx, left_right, a_ident, right_right, b_ident)
|
||||
},
|
||||
// Two unary ops, which are the same operation and which have the same argument
|
||||
(ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
|
||||
left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident)
|
||||
},
|
||||
// The two exprs are literals of some kind
|
||||
(ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
|
||||
(ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(cx, left, a_ident, right, b_ident),
|
||||
(ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
|
||||
mirrored_exprs(cx, left_block, a_ident, right_block, b_ident)
|
||||
},
|
||||
(ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
|
||||
left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident)
|
||||
},
|
||||
// Two paths: either one is a and the other is b, or they're identical to each other
|
||||
(
|
||||
ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
Path {
|
||||
segments: left_segments,
|
||||
..
|
||||
},
|
||||
)),
|
||||
ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
Path {
|
||||
segments: right_segments,
|
||||
..
|
||||
},
|
||||
)),
|
||||
) => {
|
||||
(left_segments
|
||||
.iter()
|
||||
.zip(right_segments.iter())
|
||||
.all(|(left, right)| left.ident == right.ident)
|
||||
&& left_segments
|
||||
.iter()
|
||||
.all(|seg| &seg.ident != a_ident && &seg.ident != b_ident))
|
||||
|| (left_segments.len() == 1
|
||||
&& &left_segments[0].ident == a_ident
|
||||
&& right_segments.len() == 1
|
||||
&& &right_segments[0].ident == b_ident)
|
||||
},
|
||||
// Matching expressions, but one or both is borrowed
|
||||
(
|
||||
ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
|
||||
ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
|
||||
) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident),
|
||||
(_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => {
|
||||
mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident)
|
||||
},
|
||||
(ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<LintTrigger> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(name_ident, _, args) = &expr.kind;
|
||||
if let name = name_ident.ident.name.to_ident_string();
|
||||
if name == "sort_by" || name == "sort_unstable_by";
|
||||
if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args;
|
||||
if utils::match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC);
|
||||
if let closure_body = cx.tcx.hir().body(*closure_body_id);
|
||||
if let &[
|
||||
Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
|
||||
Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
|
||||
] = &closure_body.params;
|
||||
if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr]) = &closure_body.value.kind;
|
||||
if method_path.ident.name.to_ident_string() == "cmp";
|
||||
then {
|
||||
let (closure_body, closure_arg, reverse) = if mirrored_exprs(
|
||||
&cx,
|
||||
&left_expr,
|
||||
&left_ident,
|
||||
&right_expr,
|
||||
&right_ident
|
||||
) {
|
||||
(Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string(), false)
|
||||
} else if mirrored_exprs(&cx, &left_expr, &right_ident, &right_expr, &left_ident) {
|
||||
(Sugg::hir(cx, &left_expr, "..").to_string(), right_ident.name.to_string(), true)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
|
||||
let unstable = name == "sort_unstable_by";
|
||||
if_chain! {
|
||||
if let ExprKind::Path(QPath::Resolved(_, Path {
|
||||
segments: [PathSegment { ident: left_name, .. }], ..
|
||||
})) = &left_expr.kind;
|
||||
if left_name == left_ident;
|
||||
then {
|
||||
Some(LintTrigger::Sort(SortDetection { vec_name, unstable }))
|
||||
}
|
||||
else {
|
||||
Some(LintTrigger::SortByKey(SortByKeyDetection {
|
||||
vec_name,
|
||||
unstable,
|
||||
closure_arg,
|
||||
closure_body,
|
||||
reverse
|
||||
}))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_, '_> for UnnecessarySortBy {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
|
||||
match detect_lint(cx, expr) {
|
||||
Some(LintTrigger::SortByKey(trigger)) => utils::span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort_by_key here instead",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}_by_key(|&{}| {})",
|
||||
trigger.vec_name,
|
||||
if trigger.unstable { "_unstable" } else { "" },
|
||||
trigger.closure_arg,
|
||||
if trigger.reverse {
|
||||
format!("Reverse({})", trigger.closure_body)
|
||||
} else {
|
||||
trigger.closure_body.to_string()
|
||||
},
|
||||
),
|
||||
if trigger.reverse {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
},
|
||||
),
|
||||
Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort here instead",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}()",
|
||||
trigger.vec_name,
|
||||
if trigger.unstable { "_unstable" } else { "" },
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
None => {},
|
||||
}
|
||||
}
|
||||
}
|
407
src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
Normal file
407
src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
Normal file
@ -0,0 +1,407 @@
|
||||
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
|
||||
|
||||
use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
|
||||
use crate::utils::{over, span_lint_and_then};
|
||||
use rustc_ast::ast::{self, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
|
||||
use rustc_ast::mut_visit::*;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::mem;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
///
|
||||
/// Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and
|
||||
/// suggests replacing the pattern with a nested one, `Some(0 | 2)`.
|
||||
///
|
||||
/// Another way to think of this is that it rewrites patterns in
|
||||
/// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
///
|
||||
/// In the example above, `Some` is repeated, which unncessarily complicates the pattern.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// if let Some(0) | Some(2) = Some(0) {}
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// #![feature(or_patterns)]
|
||||
///
|
||||
/// fn main() {
|
||||
/// if let Some(0 | 2) = Some(0) {}
|
||||
/// }
|
||||
/// ```
|
||||
pub UNNESTED_OR_PATTERNS,
|
||||
complexity,
|
||||
"unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]);
|
||||
|
||||
impl EarlyLintPass for UnnestedOrPatterns {
|
||||
fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
|
||||
lint_unnested_or_patterns(cx, &a.pat);
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
if let ast::ExprKind::Let(pat, _) = &e.kind {
|
||||
lint_unnested_or_patterns(cx, pat);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
|
||||
lint_unnested_or_patterns(cx, &p.pat);
|
||||
}
|
||||
|
||||
fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
|
||||
lint_unnested_or_patterns(cx, &l.pat);
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
|
||||
if !cx.sess.opts.unstable_features.is_nightly_build() {
|
||||
// User cannot do `#![feature(or_patterns)]`, so bail.
|
||||
return;
|
||||
}
|
||||
|
||||
if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind {
|
||||
// This is a leaf pattern, so cloning is unprofitable.
|
||||
return;
|
||||
}
|
||||
|
||||
let mut pat = P(pat.clone());
|
||||
|
||||
// Nix all the paren patterns everywhere so that they aren't in our way.
|
||||
remove_all_parens(&mut pat);
|
||||
|
||||
// Transform all unnested or-patterns into nested ones, and if there were none, quit.
|
||||
if !unnest_or_patterns(&mut pat) {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(cx, UNNESTED_OR_PATTERNS, pat.span, "unnested or-patterns", |db| {
|
||||
insert_necessary_parens(&mut pat);
|
||||
db.span_suggestion_verbose(
|
||||
pat.span,
|
||||
"nest the patterns",
|
||||
pprust::pat_to_string(&pat),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Remove all `(p)` patterns in `pat`.
|
||||
fn remove_all_parens(pat: &mut P<Pat>) {
|
||||
struct Visitor;
|
||||
impl MutVisitor for Visitor {
|
||||
fn visit_pat(&mut self, pat: &mut P<Pat>) {
|
||||
noop_visit_pat(pat, self);
|
||||
let inner = match &mut pat.kind {
|
||||
Paren(i) => mem::replace(&mut i.kind, Wild),
|
||||
_ => return,
|
||||
};
|
||||
pat.kind = inner;
|
||||
}
|
||||
}
|
||||
Visitor.visit_pat(pat);
|
||||
}
|
||||
|
||||
/// Insert parens where necessary according to Rust's precedence rules for patterns.
|
||||
fn insert_necessary_parens(pat: &mut P<Pat>) {
|
||||
struct Visitor;
|
||||
impl MutVisitor for Visitor {
|
||||
fn visit_pat(&mut self, pat: &mut P<Pat>) {
|
||||
use ast::{BindingMode::*, Mutability::*};
|
||||
noop_visit_pat(pat, self);
|
||||
let target = match &mut pat.kind {
|
||||
// `i @ a | b`, `box a | b`, and `& mut? a | b`.
|
||||
Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
|
||||
Ref(p, Not) if matches!(p.kind, Ident(ByValue(Mut), ..)) => p, // `&(mut x)`
|
||||
_ => return,
|
||||
};
|
||||
target.kind = Paren(P(take_pat(target)));
|
||||
}
|
||||
}
|
||||
Visitor.visit_pat(pat);
|
||||
}
|
||||
|
||||
/// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`.
|
||||
/// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`.
|
||||
fn unnest_or_patterns(pat: &mut P<Pat>) -> bool {
|
||||
struct Visitor {
|
||||
changed: bool,
|
||||
}
|
||||
impl MutVisitor for Visitor {
|
||||
fn visit_pat(&mut self, p: &mut P<Pat>) {
|
||||
// This is a bottom up transformation, so recurse first.
|
||||
noop_visit_pat(p, self);
|
||||
|
||||
// Don't have an or-pattern? Just quit early on.
|
||||
let alternatives = match &mut p.kind {
|
||||
Or(ps) => ps,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Collapse or-patterns directly nested in or-patterns.
|
||||
let mut idx = 0;
|
||||
let mut this_level_changed = false;
|
||||
while idx < alternatives.len() {
|
||||
let inner = if let Or(ps) = &mut alternatives[idx].kind {
|
||||
mem::take(ps)
|
||||
} else {
|
||||
idx += 1;
|
||||
continue;
|
||||
};
|
||||
this_level_changed = true;
|
||||
alternatives.splice(idx..=idx, inner);
|
||||
}
|
||||
|
||||
// Focus on `p_n` and then try to transform all `p_i` where `i > n`.
|
||||
let mut focus_idx = 0;
|
||||
while focus_idx < alternatives.len() {
|
||||
this_level_changed |= transform_with_focus_on_idx(alternatives, focus_idx);
|
||||
focus_idx += 1;
|
||||
}
|
||||
self.changed |= this_level_changed;
|
||||
|
||||
// Deal with `Some(Some(0)) | Some(Some(1))`.
|
||||
if this_level_changed {
|
||||
noop_visit_pat(p, self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = Visitor { changed: false };
|
||||
visitor.visit_pat(pat);
|
||||
visitor.changed
|
||||
}
|
||||
|
||||
/// Match `$scrutinee` against `$pat` and extract `$then` from it.
|
||||
/// Panics if there is no match.
|
||||
macro_rules! always_pat {
|
||||
($scrutinee:expr, $pat:pat => $then:expr) => {
|
||||
match $scrutinee {
|
||||
$pat => $then,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Focus on `focus_idx` in `alternatives`,
|
||||
/// attempting to extend it with elements of the same constructor `C`
|
||||
/// in `alternatives[focus_idx + 1..]`.
|
||||
fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize) -> bool {
|
||||
// Extract the kind; we'll need to make some changes in it.
|
||||
let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, PatKind::Wild);
|
||||
// We'll focus on `alternatives[focus_idx]`,
|
||||
// so we're draining from `alternatives[focus_idx + 1..]`.
|
||||
let start = focus_idx + 1;
|
||||
|
||||
// We're trying to find whatever kind (~"constructor") we found in `alternatives[start..]`.
|
||||
let changed = match &mut focus_kind {
|
||||
// These pattern forms are "leafs" and do not have sub-patterns.
|
||||
// Therefore they are not some form of constructor `C`,
|
||||
// with which a pattern `C(p_0)` may be formed,
|
||||
// which we would want to join with other `C(p_j)`s.
|
||||
Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_)
|
||||
// Dealt with elsewhere.
|
||||
| Or(_) | Paren(_) => false,
|
||||
// Transform `box x | ... | box y` into `box (x | y)`.
|
||||
//
|
||||
// The cases below until `Slice(...)` deal with *singleton* products.
|
||||
// These patterns have the shape `C(p)`, and not e.g., `C(p0, ..., pn)`.
|
||||
Box(target) => extend_with_matching(
|
||||
target, start, alternatives,
|
||||
|k| matches!(k, Box(_)),
|
||||
|k| always_pat!(k, Box(p) => p),
|
||||
),
|
||||
// Transform `&m x | ... | &m y` into `&m (x | y)`.
|
||||
Ref(target, m1) => extend_with_matching(
|
||||
target, start, alternatives,
|
||||
|k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match.
|
||||
|k| always_pat!(k, Ref(p, _) => p),
|
||||
),
|
||||
// Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`.
|
||||
Ident(b1, i1, Some(target)) => extend_with_matching(
|
||||
target, start, alternatives,
|
||||
// Binding names must match.
|
||||
|k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && eq_id(*i1, *i2)),
|
||||
|k| always_pat!(k, Ident(_, _, Some(p)) => p),
|
||||
),
|
||||
// Transform `[pre, x, post] | ... | [pre, y, post]` into `[pre, x | y, post]`.
|
||||
Slice(ps1) => extend_with_matching_product(
|
||||
ps1, start, alternatives,
|
||||
|k, ps1, idx| matches!(k, Slice(ps2) if eq_pre_post(ps1, ps2, idx)),
|
||||
|k| always_pat!(k, Slice(ps) => ps),
|
||||
),
|
||||
// Transform `(pre, x, post) | ... | (pre, y, post)` into `(pre, x | y, post)`.
|
||||
Tuple(ps1) => extend_with_matching_product(
|
||||
ps1, start, alternatives,
|
||||
|k, ps1, idx| matches!(k, Tuple(ps2) if eq_pre_post(ps1, ps2, idx)),
|
||||
|k| always_pat!(k, Tuple(ps) => ps),
|
||||
),
|
||||
// Transform `S(pre, x, post) | ... | S(pre, y, post)` into `S(pre, x | y, post)`.
|
||||
TupleStruct(path1, ps1) => extend_with_matching_product(
|
||||
ps1, start, alternatives,
|
||||
|k, ps1, idx| matches!(
|
||||
k,
|
||||
TupleStruct(path2, ps2) if eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
|
||||
),
|
||||
|k| always_pat!(k, TupleStruct(_, ps) => ps),
|
||||
),
|
||||
// Transform a record pattern `S { fp_0, ..., fp_n }`.
|
||||
Struct(path1, fps1, rest1) => extend_with_struct_pat(path1, fps1, *rest1, start, alternatives),
|
||||
};
|
||||
|
||||
alternatives[focus_idx].kind = focus_kind;
|
||||
changed
|
||||
}
|
||||
|
||||
/// Here we focusing on a record pattern `S { fp_0, ..., fp_n }`.
|
||||
/// In particular, for a record pattern, the order in which the field patterns is irrelevant.
|
||||
/// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern
|
||||
/// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal.
|
||||
fn extend_with_struct_pat(
|
||||
path1: &ast::Path,
|
||||
fps1: &mut Vec<ast::FieldPat>,
|
||||
rest1: bool,
|
||||
start: usize,
|
||||
alternatives: &mut Vec<P<Pat>>,
|
||||
) -> bool {
|
||||
(0..fps1.len()).any(|idx| {
|
||||
let pos_in_2 = Cell::new(None); // The element `k`.
|
||||
let tail_or = drain_matching(
|
||||
start,
|
||||
alternatives,
|
||||
|k| {
|
||||
matches!(k, Struct(path2, fps2, rest2)
|
||||
if rest1 == *rest2 // If one struct pattern has `..` so must the other.
|
||||
&& eq_path(path1, path2)
|
||||
&& fps1.len() == fps2.len()
|
||||
&& fps1.iter().enumerate().all(|(idx_1, fp1)| {
|
||||
if idx_1 == idx {
|
||||
// In the case of `k`, we merely require identical field names
|
||||
// so that we will transform into `ident_k: p1_k | p2_k`.
|
||||
let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident));
|
||||
pos_in_2.set(pos);
|
||||
pos.is_some()
|
||||
} else {
|
||||
fps2.iter().any(|fp2| eq_field_pat(fp1, fp2))
|
||||
}
|
||||
}))
|
||||
},
|
||||
// Extract `p2_k`.
|
||||
|k| always_pat!(k, Struct(_, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat),
|
||||
);
|
||||
extend_with_tail_or(&mut fps1[idx].pat, tail_or)
|
||||
})
|
||||
}
|
||||
|
||||
/// Like `extend_with_matching` but for products with > 1 factor, e.g., `C(p_0, ..., p_n)`.
|
||||
/// Here, the idea is that we fixate on some `p_k` in `C`,
|
||||
/// allowing it to vary between two `targets` and `ps2` (returned by `extract`),
|
||||
/// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post),
|
||||
/// where `~` denotes semantic equality.
|
||||
fn extend_with_matching_product(
|
||||
targets: &mut Vec<P<Pat>>,
|
||||
start: usize,
|
||||
alternatives: &mut Vec<P<Pat>>,
|
||||
predicate: impl Fn(&PatKind, &[P<Pat>], usize) -> bool,
|
||||
extract: impl Fn(PatKind) -> Vec<P<Pat>>,
|
||||
) -> bool {
|
||||
(0..targets.len()).any(|idx| {
|
||||
let tail_or = drain_matching(
|
||||
start,
|
||||
alternatives,
|
||||
|k| predicate(k, targets, idx),
|
||||
|k| extract(k).swap_remove(idx),
|
||||
);
|
||||
extend_with_tail_or(&mut targets[idx], tail_or)
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract the pattern from the given one and replace it with `Wild`.
|
||||
/// This is meant for temporarily swapping out the pattern for manipulation.
|
||||
fn take_pat(from: &mut Pat) -> Pat {
|
||||
let dummy = Pat {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: Wild,
|
||||
span: DUMMY_SP,
|
||||
};
|
||||
mem::replace(from, dummy)
|
||||
}
|
||||
|
||||
/// Extend `target` as an or-pattern with the alternatives
|
||||
/// in `tail_or` if there are any and return if there were.
|
||||
fn extend_with_tail_or(target: &mut Pat, tail_or: Vec<P<Pat>>) -> bool {
|
||||
fn extend(target: &mut Pat, mut tail_or: Vec<P<Pat>>) {
|
||||
match target {
|
||||
// On an existing or-pattern in the target, append to it.
|
||||
Pat { kind: Or(ps), .. } => ps.append(&mut tail_or),
|
||||
// Otherwise convert the target to an or-pattern.
|
||||
target => {
|
||||
let mut init_or = vec![P(take_pat(target))];
|
||||
init_or.append(&mut tail_or);
|
||||
target.kind = Or(init_or);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let changed = !tail_or.is_empty();
|
||||
if changed {
|
||||
// Extend the target.
|
||||
extend(target, tail_or);
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
// Extract all inner patterns in `alternatives` matching our `predicate`.
|
||||
// Only elements beginning with `start` are considered for extraction.
|
||||
fn drain_matching(
|
||||
start: usize,
|
||||
alternatives: &mut Vec<P<Pat>>,
|
||||
predicate: impl Fn(&PatKind) -> bool,
|
||||
extract: impl Fn(PatKind) -> P<Pat>,
|
||||
) -> Vec<P<Pat>> {
|
||||
let mut tail_or = vec![];
|
||||
let mut idx = 0;
|
||||
for pat in alternatives.drain_filter(|p| {
|
||||
// Check if we should extract, but only if `idx >= start`.
|
||||
idx += 1;
|
||||
idx > start && predicate(&p.kind)
|
||||
}) {
|
||||
tail_or.push(extract(pat.into_inner().kind));
|
||||
}
|
||||
tail_or
|
||||
}
|
||||
|
||||
fn extend_with_matching(
|
||||
target: &mut Pat,
|
||||
start: usize,
|
||||
alternatives: &mut Vec<P<Pat>>,
|
||||
predicate: impl Fn(&PatKind) -> bool,
|
||||
extract: impl Fn(PatKind) -> P<Pat>,
|
||||
) -> bool {
|
||||
extend_with_tail_or(target, drain_matching(start, alternatives, predicate, extract))
|
||||
}
|
||||
|
||||
/// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`?
|
||||
fn eq_pre_post(ps1: &[P<Pat>], ps2: &[P<Pat>], idx: usize) -> bool {
|
||||
ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
|
||||
&& ps1.len() == ps2.len()
|
||||
&& over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r))
|
||||
&& over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r))
|
||||
}
|
@ -101,7 +101,7 @@ fn collect_unwrap_info<'a, 'tcx>(
|
||||
|
||||
if let ExprKind::Binary(op, left, right) = &expr.kind {
|
||||
match (invert, op.node) {
|
||||
(false, BinOpKind::And) | (false, BinOpKind::BitAnd) | (true, BinOpKind::Or) | (true, BinOpKind::BitOr) => {
|
||||
(false, BinOpKind::And | BinOpKind::BitAnd) | (true, BinOpKind::Or | BinOpKind::BitOr) => {
|
||||
let mut unwrap_info = collect_unwrap_info(cx, left, branch, invert);
|
||||
unwrap_info.append(&mut collect_unwrap_info(cx, right, branch, invert));
|
||||
return unwrap_info;
|
||||
|
@ -1,12 +1,12 @@
|
||||
use crate::utils::{
|
||||
is_type_diagnostic_item, match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite,
|
||||
is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite,
|
||||
span_lint_and_help, span_lint_and_sugg,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, HirId, MatchSource};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{self, TyS};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -65,7 +65,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion {
|
||||
if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" {
|
||||
let a = cx.tables.expr_ty(e);
|
||||
let b = cx.tables.expr_ty(&args[0]);
|
||||
if same_tys(cx, a, b) {
|
||||
if TyS::same_type(a, b) {
|
||||
let sugg = snippet_with_macro_callsite(cx, args[0].span, "<expr>").to_string();
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
@ -81,7 +81,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion {
|
||||
if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" {
|
||||
let a = cx.tables.expr_ty(e);
|
||||
let b = cx.tables.expr_ty(&args[0]);
|
||||
if same_tys(cx, a, b) {
|
||||
if TyS::same_type(a, b) {
|
||||
let sugg = snippet(cx, args[0].span, "<expr>").into_owned();
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
@ -101,7 +101,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion {
|
||||
if is_type_diagnostic_item(cx, a, sym!(result_type));
|
||||
if let ty::Adt(_, substs) = a.kind;
|
||||
if let Some(a_type) = substs.types().next();
|
||||
if same_tys(cx, a_type, b);
|
||||
if TyS::same_type(a_type, b);
|
||||
|
||||
then {
|
||||
span_lint_and_help(
|
||||
@ -131,7 +131,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion {
|
||||
if is_type_diagnostic_item(cx, a, sym!(result_type));
|
||||
if let ty::Adt(_, substs) = a.kind;
|
||||
if let Some(a_type) = substs.types().next();
|
||||
if same_tys(cx, a_type, b);
|
||||
if TyS::same_type(a_type, b);
|
||||
|
||||
then {
|
||||
let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from"));
|
||||
@ -148,7 +148,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion {
|
||||
|
||||
if_chain! {
|
||||
if match_def_path(cx, def_id, &paths::FROM_FROM);
|
||||
if same_tys(cx, a, b);
|
||||
if TyS::same_type(a, b);
|
||||
|
||||
then {
|
||||
let sugg = snippet(cx, args[0].span.source_callsite(), "<expr>").into_owned();
|
||||
|
526
src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
Executable file
526
src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
Executable file
@ -0,0 +1,526 @@
|
||||
//! Utilities for manipulating and extracting information from `rustc_ast::ast`.
|
||||
//!
|
||||
//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s.
|
||||
|
||||
#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
|
||||
|
||||
use crate::utils::{both, over};
|
||||
use rustc_ast::ast::{self, *};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::mem;
|
||||
|
||||
/// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
|
||||
pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
|
||||
left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
|
||||
}
|
||||
|
||||
pub fn eq_id(l: Ident, r: Ident) -> bool {
|
||||
l.name == r.name
|
||||
}
|
||||
|
||||
pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
|
||||
use PatKind::*;
|
||||
match (&l.kind, &r.kind) {
|
||||
(Paren(l), _) => eq_pat(l, r),
|
||||
(_, Paren(r)) => eq_pat(l, r),
|
||||
(Wild, Wild) | (Rest, Rest) => true,
|
||||
(Lit(l), Lit(r)) => eq_expr(l, r),
|
||||
(Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)),
|
||||
(Range(lf, lt, le), Range(rf, rt, re)) => {
|
||||
eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node)
|
||||
},
|
||||
(Box(l), Box(r))
|
||||
| (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
|
||||
| (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
|
||||
(Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
|
||||
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
|
||||
(TupleStruct(lp, lfs), TupleStruct(rp, rfs)) => eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)),
|
||||
(Struct(lp, lfs, lr), Struct(rp, rfs, rr)) => {
|
||||
lr == rr && eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
|
||||
},
|
||||
(Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool {
|
||||
match (l, r) {
|
||||
(RangeEnd::Excluded, RangeEnd::Excluded) => true,
|
||||
(RangeEnd::Included(l), RangeEnd::Included(r)) => {
|
||||
matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_field_pat(l: &FieldPat, r: &FieldPat) -> bool {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& eq_id(l.ident, r.ident)
|
||||
&& eq_pat(&l.pat, &r.pat)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
|
||||
l.position == r.position && eq_ty(&l.ty, &r.ty)
|
||||
}
|
||||
|
||||
pub fn eq_path(l: &Path, r: &Path) -> bool {
|
||||
over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
|
||||
eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool {
|
||||
match (l, r) {
|
||||
(GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => {
|
||||
over(&l.args, &r.args, |l, r| eq_angle_arg(l, r))
|
||||
},
|
||||
(GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => {
|
||||
over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool {
|
||||
match (l, r) {
|
||||
(AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r),
|
||||
(AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool {
|
||||
match (l, r) {
|
||||
(GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident),
|
||||
(GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r),
|
||||
(GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_expr_opt(l: &Option<P<Expr>>, r: &Option<P<Expr>>) -> bool {
|
||||
both(l, r, |l, r| eq_expr(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
|
||||
use ExprKind::*;
|
||||
if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) {
|
||||
return false;
|
||||
}
|
||||
match (&l.kind, &r.kind) {
|
||||
(Paren(l), _) => eq_expr(l, r),
|
||||
(_, Paren(r)) => eq_expr(l, r),
|
||||
(Err, Err) => true,
|
||||
(Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r),
|
||||
(Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
|
||||
(Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value),
|
||||
(Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
|
||||
(MethodCall(lc, la), MethodCall(rc, ra)) => eq_path_seg(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
|
||||
(Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr),
|
||||
(Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
|
||||
(Lit(l), Lit(r)) => l.kind == r.kind,
|
||||
(Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
|
||||
(Let(lp, le), Let(rp, re)) => eq_pat(lp, rp) && eq_expr(le, re),
|
||||
(If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
|
||||
(While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt),
|
||||
(ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
|
||||
eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt)
|
||||
},
|
||||
(Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt),
|
||||
(Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb),
|
||||
(TryBlock(l), TryBlock(r)) => eq_block(l, r),
|
||||
(Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r),
|
||||
(Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re),
|
||||
(Continue(ll), Continue(rl)) => eq_label(ll, rl),
|
||||
(Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2),
|
||||
(AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
|
||||
(Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
|
||||
(Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)),
|
||||
(Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => {
|
||||
lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb)
|
||||
},
|
||||
(Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
|
||||
(Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
|
||||
(AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
|
||||
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
(Struct(lp, lfs, lb), Struct(rp, rfs, rb)) => {
|
||||
eq_path(lp, rp) && eq_expr_opt(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r))
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_field(l: &Field, r: &Field) -> bool {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& eq_id(l.ident, r.ident)
|
||||
&& eq_expr(&l.expr, &r.expr)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& eq_pat(&l.pat, &r.pat)
|
||||
&& eq_expr(&l.body, &r.body)
|
||||
&& eq_expr_opt(&l.guard, &r.guard)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
|
||||
both(l, r, |l, r| eq_id(l.ident, r.ident))
|
||||
}
|
||||
|
||||
pub fn eq_block(l: &Block, r: &Block) -> bool {
|
||||
l.rules == r.rules && over(&l.stmts, &r.stmts, |l, r| eq_stmt(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
|
||||
use StmtKind::*;
|
||||
match (&l.kind, &r.kind) {
|
||||
(Local(l), Local(r)) => {
|
||||
eq_pat(&l.pat, &r.pat)
|
||||
&& both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
|
||||
&& eq_expr_opt(&l.init, &r.init)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
},
|
||||
(Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
|
||||
(Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
|
||||
(Empty, Empty) => true,
|
||||
(MacCall(l), MacCall(r)) => l.1 == r.1 && eq_mac_call(&l.0, &r.0) && over(&l.2, &r.2, |l, r| eq_attr(l, r)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
|
||||
eq_id(l.ident, r.ident)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
&& eq_vis(&l.vis, &r.vis)
|
||||
&& eq_kind(&l.kind, &r.kind)
|
||||
}
|
||||
|
||||
pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
|
||||
use ItemKind::*;
|
||||
match (l, r) {
|
||||
(ExternCrate(l), ExternCrate(r)) => l == r,
|
||||
(Use(l), Use(r)) => eq_use_tree(l, r),
|
||||
(Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
|
||||
(Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
|
||||
(Fn(ld, lf, lg, lb), Fn(rd, rf, rg, rb)) => {
|
||||
eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
|
||||
},
|
||||
(Mod(l), Mod(r)) => l.inline == r.inline && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_item_kind)),
|
||||
(ForeignMod(l), ForeignMod(r)) => {
|
||||
both(&l.abi, &r.abi, |l, r| eq_str_lit(l, r))
|
||||
&& over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
|
||||
},
|
||||
(TyAlias(ld, lg, lb, lt), TyAlias(rd, rg, rb, rt)) => {
|
||||
eq_defaultness(*ld, *rd)
|
||||
&& eq_generics(lg, rg)
|
||||
&& over(lb, rb, |l, r| eq_generic_bound(l, r))
|
||||
&& both(lt, rt, |l, r| eq_ty(l, r))
|
||||
},
|
||||
(Enum(le, lg), Enum(re, rg)) => {
|
||||
over(&le.variants, &re.variants, |l, r| eq_variant(l, r)) && eq_generics(lg, rg)
|
||||
},
|
||||
(Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
|
||||
eq_variant_data(lv, rv) && eq_generics(lg, rg)
|
||||
},
|
||||
(Trait(la, lu, lg, lb, li), Trait(ra, ru, rg, rb, ri)) => {
|
||||
la == ra
|
||||
&& matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
|
||||
&& eq_generics(lg, rg)
|
||||
&& over(lb, rb, |l, r| eq_generic_bound(l, r))
|
||||
&& over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
|
||||
},
|
||||
(TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, |l, r| eq_generic_bound(l, r)),
|
||||
(
|
||||
Impl {
|
||||
unsafety: lu,
|
||||
polarity: lp,
|
||||
defaultness: ld,
|
||||
constness: lc,
|
||||
generics: lg,
|
||||
of_trait: lot,
|
||||
self_ty: lst,
|
||||
items: li,
|
||||
},
|
||||
Impl {
|
||||
unsafety: ru,
|
||||
polarity: rp,
|
||||
defaultness: rd,
|
||||
constness: rc,
|
||||
generics: rg,
|
||||
of_trait: rot,
|
||||
self_ty: rst,
|
||||
items: ri,
|
||||
},
|
||||
) => {
|
||||
matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
|
||||
&& matches!(lp, ImplPolarity::Positive) == matches!(rp, ImplPolarity::Positive)
|
||||
&& eq_defaultness(*ld, *rd)
|
||||
&& matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
|
||||
&& eq_generics(lg, rg)
|
||||
&& both(lot, rot, |l, r| eq_path(&l.path, &r.path))
|
||||
&& eq_ty(lst, rst)
|
||||
&& over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
|
||||
},
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
(MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_mac_args(&l.body, &r.body),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
|
||||
use ForeignItemKind::*;
|
||||
match (l, r) {
|
||||
(Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
|
||||
(Fn(ld, lf, lg, lb), Fn(rd, rf, rg, rb)) => {
|
||||
eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
|
||||
},
|
||||
(TyAlias(ld, lg, lb, lt), TyAlias(rd, rg, rb, rt)) => {
|
||||
eq_defaultness(*ld, *rd)
|
||||
&& eq_generics(lg, rg)
|
||||
&& over(lb, rb, |l, r| eq_generic_bound(l, r))
|
||||
&& both(lt, rt, |l, r| eq_ty(l, r))
|
||||
},
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
|
||||
use AssocItemKind::*;
|
||||
match (l, r) {
|
||||
(Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
|
||||
(Fn(ld, lf, lg, lb), Fn(rd, rf, rg, rb)) => {
|
||||
eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
|
||||
},
|
||||
(TyAlias(ld, lg, lb, lt), TyAlias(rd, rg, rb, rt)) => {
|
||||
eq_defaultness(*ld, *rd)
|
||||
&& eq_generics(lg, rg)
|
||||
&& over(lb, rb, |l, r| eq_generic_bound(l, r))
|
||||
&& both(lt, rt, |l, r| eq_ty(l, r))
|
||||
},
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_variant(l: &Variant, r: &Variant) -> bool {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
&& eq_vis(&l.vis, &r.vis)
|
||||
&& eq_id(l.ident, r.ident)
|
||||
&& eq_variant_data(&l.data, &r.data)
|
||||
&& both(&l.disr_expr, &r.disr_expr, |l, r| eq_expr(&l.value, &r.value))
|
||||
}
|
||||
|
||||
pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool {
|
||||
use VariantData::*;
|
||||
match (l, r) {
|
||||
(Unit(_), Unit(_)) => true,
|
||||
(Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, |l, r| eq_struct_field(l, r)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_struct_field(l: &StructField, r: &StructField) -> bool {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
&& eq_vis(&l.vis, &r.vis)
|
||||
&& both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
|
||||
&& eq_ty(&l.ty, &r.ty)
|
||||
}
|
||||
|
||||
pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
|
||||
eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header)
|
||||
}
|
||||
|
||||
pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
|
||||
matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No)
|
||||
&& l.asyncness.is_async() == r.asyncness.is_async()
|
||||
&& matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
|
||||
&& eq_ext(&l.ext, &r.ext)
|
||||
}
|
||||
|
||||
pub fn eq_generics(l: &Generics, r: &Generics) -> bool {
|
||||
over(&l.params, &r.params, |l, r| eq_generic_param(l, r))
|
||||
&& over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| {
|
||||
eq_where_predicate(l, r)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
|
||||
use WherePredicate::*;
|
||||
match (l, r) {
|
||||
(BoundPredicate(l), BoundPredicate(r)) => {
|
||||
over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
|
||||
eq_generic_param(l, r)
|
||||
}) && eq_ty(&l.bounded_ty, &r.bounded_ty)
|
||||
&& over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
|
||||
},
|
||||
(RegionPredicate(l), RegionPredicate(r)) => {
|
||||
eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
|
||||
},
|
||||
(EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool {
|
||||
eq_path(&l.prefix, &r.prefix) && eq_use_tree_kind(&l.kind, &r.kind)
|
||||
}
|
||||
|
||||
pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
|
||||
use UseTreeKind::*;
|
||||
match (l, r) {
|
||||
(Glob, Glob) => true,
|
||||
(Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)),
|
||||
(Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
|
||||
match (l, r) {
|
||||
(Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
|
||||
use VisibilityKind::*;
|
||||
match (&l.node, &r.node) {
|
||||
(Public, Public) | (Inherited, Inherited) | (Crate(_), Crate(_)) => true,
|
||||
(Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool {
|
||||
eq_fn_ret_ty(&l.output, &r.output)
|
||||
&& over(&l.inputs, &r.inputs, |l, r| {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& eq_pat(&l.pat, &r.pat)
|
||||
&& eq_ty(&l.ty, &r.ty)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn eq_fn_ret_ty(l: &FnRetTy, r: &FnRetTy) -> bool {
|
||||
match (l, r) {
|
||||
(FnRetTy::Default(_), FnRetTy::Default(_)) => true,
|
||||
(FnRetTy::Ty(l), FnRetTy::Ty(r)) => eq_ty(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
|
||||
use TyKind::*;
|
||||
match (&l.kind, &r.kind) {
|
||||
(Paren(l), _) => eq_ty(l, r),
|
||||
(_, Paren(r)) => eq_ty(l, r),
|
||||
(Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err, Err) | (CVarArgs, CVarArgs) => true,
|
||||
(Slice(l), Slice(r)) => eq_ty(l, r),
|
||||
(Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value),
|
||||
(Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty),
|
||||
(Rptr(ll, l), Rptr(rl, r)) => {
|
||||
both(ll, rl, |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty)
|
||||
},
|
||||
(BareFn(l), BareFn(r)) => {
|
||||
l.unsafety == r.unsafety
|
||||
&& eq_ext(&l.ext, &r.ext)
|
||||
&& over(&l.generic_params, &r.generic_params, |l, r| eq_generic_param(l, r))
|
||||
&& eq_fn_decl(&l.decl, &r.decl)
|
||||
},
|
||||
(Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
|
||||
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
|
||||
(TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, |l, r| eq_generic_bound(l, r)),
|
||||
(ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, |l, r| eq_generic_bound(l, r)),
|
||||
(Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_ext(l: &Extern, r: &Extern) -> bool {
|
||||
use Extern::*;
|
||||
match (l, r) {
|
||||
(None, None) | (Implicit, Implicit) => true,
|
||||
(Explicit(l), Explicit(r)) => eq_str_lit(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_str_lit(l: &StrLit, r: &StrLit) -> bool {
|
||||
l.style == r.style && l.symbol == r.symbol && l.suffix == r.suffix
|
||||
}
|
||||
|
||||
pub fn eq_poly_ref_trait(l: &PolyTraitRef, r: &PolyTraitRef) -> bool {
|
||||
eq_path(&l.trait_ref.path, &r.trait_ref.path)
|
||||
&& over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
|
||||
eq_generic_param(l, r)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
|
||||
use GenericParamKind::*;
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& eq_id(l.ident, r.ident)
|
||||
&& over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
|
||||
&& match (&l.kind, &r.kind) {
|
||||
(Lifetime, Lifetime) => true,
|
||||
(Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
|
||||
(Const { ty: l }, Const { ty: r }) => eq_ty(l, r),
|
||||
_ => false,
|
||||
}
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
|
||||
use GenericBound::*;
|
||||
match (l, r) {
|
||||
(Trait(ptr1, tbm1), Trait(ptr2, tbm2)) => tbm1 == tbm2 && eq_poly_ref_trait(ptr1, ptr2),
|
||||
(Outlives(l), Outlives(r)) => eq_id(l.ident, r.ident),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_assoc_constraint(l: &AssocTyConstraint, r: &AssocTyConstraint) -> bool {
|
||||
use AssocTyConstraintKind::*;
|
||||
eq_id(l.ident, r.ident)
|
||||
&& match (&l.kind, &r.kind) {
|
||||
(Equality { ty: l }, Equality { ty: r }) => eq_ty(l, r),
|
||||
(Bound { bounds: l }, Bound { bounds: r }) => over(l, r, |l, r| eq_generic_bound(l, r)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool {
|
||||
eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args)
|
||||
}
|
||||
|
||||
pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
|
||||
use AttrKind::*;
|
||||
l.style == r.style
|
||||
&& match (&l.kind, &r.kind) {
|
||||
(DocComment(l), DocComment(r)) => l == r,
|
||||
(Normal(l), Normal(r)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
|
||||
use MacArgs::*;
|
||||
match (l, r) {
|
||||
(Empty, Empty) => true,
|
||||
(Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts),
|
||||
(Eq(_, lts), Eq(_, rts)) => lts.eq_unspanned(rts),
|
||||
_ => false,
|
||||
}
|
||||
}
|
@ -332,19 +332,13 @@ fn swap_binop<'a>(
|
||||
|
||||
/// Checks if the two `Option`s are both `None` or some equal values as per
|
||||
/// `eq_fn`.
|
||||
fn both<X, F>(l: &Option<X>, r: &Option<X>, mut eq_fn: F) -> bool
|
||||
where
|
||||
F: FnMut(&X, &X) -> bool,
|
||||
{
|
||||
pub fn both<X>(l: &Option<X>, r: &Option<X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
|
||||
l.as_ref()
|
||||
.map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
|
||||
}
|
||||
|
||||
/// Checks if two slices are equal as per `eq_fn`.
|
||||
fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool
|
||||
where
|
||||
F: FnMut(&X, &X) -> bool,
|
||||
{
|
||||
pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
|
||||
left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#[macro_use]
|
||||
pub mod sym;
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub mod ast_utils;
|
||||
pub mod attrs;
|
||||
pub mod author;
|
||||
pub mod camel_case;
|
||||
@ -19,7 +21,7 @@ pub mod sugg;
|
||||
pub mod usage;
|
||||
pub use self::attrs::*;
|
||||
pub use self::diagnostics::*;
|
||||
pub use self::hir_utils::{SpanlessEq, SpanlessHash};
|
||||
pub use self::hir_utils::{both, over, SpanlessEq, SpanlessHash};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::mem;
|
||||
@ -40,7 +42,7 @@ use rustc_hir::{
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Binder, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::original_sp;
|
||||
use rustc_span::symbol::{self, kw, Symbol};
|
||||
@ -72,7 +74,7 @@ pub fn in_constant(cx: &LateContext<'_, '_>, id: HirId) -> bool {
|
||||
let parent_id = cx.tcx.hir().get_parent_item(id);
|
||||
match cx.tcx.hir().get(parent_id) {
|
||||
Node::Item(&Item {
|
||||
kind: ItemKind::Const(..),
|
||||
kind: ItemKind::Const(..) | ItemKind::Static(..),
|
||||
..
|
||||
})
|
||||
| Node::TraitItem(&TraitItem {
|
||||
@ -83,11 +85,7 @@ pub fn in_constant(cx: &LateContext<'_, '_>, id: HirId) -> bool {
|
||||
kind: ImplItemKind::Const(..),
|
||||
..
|
||||
})
|
||||
| Node::AnonConst(_)
|
||||
| Node::Item(&Item {
|
||||
kind: ItemKind::Static(..),
|
||||
..
|
||||
}) => true,
|
||||
| Node::AnonConst(_) => true,
|
||||
Node::Item(&Item {
|
||||
kind: ItemKind::Fn(ref sig, ..),
|
||||
..
|
||||
@ -165,8 +163,8 @@ pub fn match_trait_method(cx: &LateContext<'_, '_>, expr: &Expr<'_>, path: &[&st
|
||||
/// Checks if an expression references a variable of the given name.
|
||||
pub fn match_var(expr: &Expr<'_>, var: Name) -> bool {
|
||||
if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
|
||||
if path.segments.len() == 1 && path.segments[0].ident.name == var {
|
||||
return true;
|
||||
if let [p] = path.segments {
|
||||
return p.ident.name == var;
|
||||
}
|
||||
}
|
||||
false
|
||||
@ -181,8 +179,7 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
|
||||
|
||||
pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
|
||||
match *path {
|
||||
QPath::Resolved(_, ref path) if path.segments.len() == 1 => Some(&path.segments[0]),
|
||||
QPath::Resolved(..) => None,
|
||||
QPath::Resolved(_, ref path) => path.segments.get(0),
|
||||
QPath::TypeRelative(_, ref seg) => Some(seg),
|
||||
}
|
||||
}
|
||||
@ -201,9 +198,12 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
|
||||
QPath::Resolved(_, ref path) => match_path(path, segments),
|
||||
QPath::TypeRelative(ref ty, ref segment) => match ty.kind {
|
||||
TyKind::Path(ref inner_path) => {
|
||||
!segments.is_empty()
|
||||
&& match_qpath(inner_path, &segments[..(segments.len() - 1)])
|
||||
&& segment.ident.name.as_str() == segments[segments.len() - 1]
|
||||
if let [prefix @ .., end] = segments {
|
||||
if match_qpath(inner_path, prefix) {
|
||||
return segment.ident.name.as_str() == *end;
|
||||
}
|
||||
}
|
||||
false
|
||||
},
|
||||
_ => false,
|
||||
},
|
||||
@ -398,7 +398,7 @@ pub fn method_calls<'tcx>(
|
||||
/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
|
||||
///
|
||||
/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
|
||||
/// `matched_method_chain(expr, &["bar", "baz"])` will return a `Vec`
|
||||
/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
|
||||
/// containing the `Expr`s for
|
||||
/// `.bar()` and `.baz()`
|
||||
pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {
|
||||
@ -882,20 +882,6 @@ pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: hir::HirId) -> T
|
||||
cx.tcx.erase_late_bound_regions(&ret_ty)
|
||||
}
|
||||
|
||||
/// Checks if two types are the same.
|
||||
///
|
||||
/// This discards any lifetime annotations, too.
|
||||
//
|
||||
// FIXME: this works correctly for lifetimes bounds (`for <'a> Foo<'a>` ==
|
||||
// `for <'b> Foo<'b>`, but not for type parameters).
|
||||
pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
|
||||
let a = cx.tcx.erase_late_bound_regions(&Binder::bind(a));
|
||||
let b = cx.tcx.erase_late_bound_regions(&Binder::bind(b));
|
||||
cx.tcx
|
||||
.infer_ctxt()
|
||||
.enter(|infcx| infcx.can_eq(cx.param_env, a, b).is_ok())
|
||||
}
|
||||
|
||||
/// Returns `true` if the given type is an `unsafe` function.
|
||||
pub fn type_is_unsafe_function<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
match ty.kind {
|
||||
@ -1408,6 +1394,24 @@ pub fn run_lints(cx: &LateContext<'_, '_>, lints: &[&'static Lint], id: HirId) -
|
||||
})
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unwrap_cargo_metadata {
|
||||
($cx: ident, $lint: ident, $deps: expr) => {{
|
||||
let mut command = cargo_metadata::MetadataCommand::new();
|
||||
if !$deps {
|
||||
command.no_deps();
|
||||
}
|
||||
|
||||
match command.exec() {
|
||||
Ok(metadata) => metadata,
|
||||
Err(err) => {
|
||||
span_lint($cx, $lint, DUMMY_SP, &format!("could not read cargo metadata: {}", err));
|
||||
return;
|
||||
},
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{trim_multiline, without_block_comments};
|
||||
|
@ -138,5 +138,6 @@ pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
|
||||
pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
|
||||
pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
|
||||
pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
|
||||
pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
|
||||
pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
|
||||
pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
|
||||
|
@ -17,8 +17,14 @@ declare_clippy_lint! {
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// foo(&vec![1, 2])
|
||||
/// ```rust
|
||||
/// # fn foo(my_vec: &[u8]) {}
|
||||
///
|
||||
/// // Bad
|
||||
/// foo(&vec![1, 2]);
|
||||
///
|
||||
/// // Good
|
||||
/// foo(&[1, 2]);
|
||||
/// ```
|
||||
pub USELESS_VEC,
|
||||
perf,
|
||||
|
59
src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs
Normal file
59
src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use crate::utils::span_lint_and_then;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use crate::utils::{match_def_path, paths};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir as hir;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Finds occurences of `Vec::resize(0, an_int)`
|
||||
///
|
||||
/// **Why is this bad?** This is probably an argument inversion mistake.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// vec!(1, 2, 3, 4, 5).resize(0, 5)
|
||||
/// ```
|
||||
pub VEC_RESIZE_TO_ZERO,
|
||||
correctness,
|
||||
"emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
|
||||
}
|
||||
|
||||
declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]);
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VecResizeToZero {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::MethodCall(path_segment, _, ref args) = expr.kind;
|
||||
if let Some(method_def_id) = cx.tables.type_dependent_def_id(expr.hir_id);
|
||||
if match_def_path(cx, method_def_id, &paths::VEC_RESIZE) && args.len() == 3;
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind;
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = args[2].kind;
|
||||
then {
|
||||
let method_call_span = expr.span.with_lo(path_segment.ident.span.lo());
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
VEC_RESIZE_TO_ZERO,
|
||||
expr.span,
|
||||
"emptying a vector with `resize`",
|
||||
|db| {
|
||||
db.help("the arguments may be inverted...");
|
||||
db.span_suggestion(
|
||||
method_call_span,
|
||||
"...or you can empty the vector with",
|
||||
"clear()".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Why is this bad?** `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
|
||||
/// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
|
@ -34,12 +34,7 @@ impl LateLintPass<'_, '_> for WildcardDependencies {
|
||||
return;
|
||||
}
|
||||
|
||||
let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() {
|
||||
metadata
|
||||
} else {
|
||||
span_lint(cx, WILDCARD_DEPENDENCIES, DUMMY_SP, "could not read cargo metadata");
|
||||
return;
|
||||
};
|
||||
let metadata = unwrap_cargo_metadata!(cx, WILDCARD_DEPENDENCIES, false);
|
||||
|
||||
for dep in &metadata.packages[0].dependencies {
|
||||
// VersionReq::any() does not work
|
||||
|
@ -19,8 +19,14 @@ declare_clippy_lint! {
|
||||
/// still around.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// use std::cmp::Ordering::*;
|
||||
/// foo(Less);
|
||||
///
|
||||
/// // Good
|
||||
/// use std::cmp::Ordering;
|
||||
/// foo(Ordering::Less)
|
||||
/// ```
|
||||
pub ENUM_GLOB_USE,
|
||||
pedantic,
|
||||
@ -60,15 +66,15 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// Bad:
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// use crate1::*;
|
||||
///
|
||||
/// foo();
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust,ignore
|
||||
/// // Good
|
||||
/// use crate1::foo;
|
||||
///
|
||||
/// foo();
|
||||
|
@ -14,7 +14,11 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// 0.0f32 / 0.0;
|
||||
/// // Bad
|
||||
/// let nan = 0.0f32 / 0.0;
|
||||
///
|
||||
/// // Good
|
||||
/// let nan = f32::NAN;
|
||||
/// ```
|
||||
pub ZERO_DIVIDED_BY_ZERO,
|
||||
complexity,
|
||||
|
@ -18,7 +18,7 @@ been very rare that Clippy changes were included in a patch release.
|
||||
|
||||
### 1. Finding the relevant Clippy commits
|
||||
|
||||
Each Rust release ships with its own version of Clippy. The Clippy submodule can
|
||||
Each Rust release ships with its own version of Clippy. The Clippy subtree can
|
||||
be found in the `tools` directory of the Rust repository.
|
||||
|
||||
Depending on the current time and what exactly you want to update, the following
|
||||
@ -32,8 +32,10 @@ bullet points might be helpful:
|
||||
need to select the Rust release tag from the dropdown and then check the
|
||||
commit of the Clippy directory:
|
||||
|
||||
![Explanation of how to find the commit hash](https://user-images.githubusercontent.com/2042399/62846160-1f8b0480-bcce-11e9-9da8-7964ca034e7a.png)
|
||||
|
||||
To find the commit hash, issue the following command when in a `rust-lang/rust` checkout:
|
||||
```
|
||||
git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g"
|
||||
```
|
||||
|
||||
### 2. Fetching the PRs between those commits
|
||||
|
||||
@ -74,5 +76,5 @@ relevant commit ranges.
|
||||
|
||||
[changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md
|
||||
[forge]: https://forge.rust-lang.org/
|
||||
[rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools
|
||||
[rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools
|
||||
[rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools/clippy
|
||||
[rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy
|
||||
|
@ -4,7 +4,9 @@ You may need following tooltips to catch up with common operations.
|
||||
|
||||
- [Common tools for writing lints](#common-tools-for-writing-lints)
|
||||
- [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
|
||||
- [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
|
||||
- [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
|
||||
- [Checking if a type defines a method](#checking-if-a-type-defines-a-method)
|
||||
- [Dealing with macros](#dealing-with-macros)
|
||||
|
||||
Useful Rustc dev guide links:
|
||||
@ -49,6 +51,26 @@ Two noticeable items here:
|
||||
- `tables` is [`TypeckTables`][TypeckTables] and is created by type checking step,
|
||||
it includes useful information such as types of expressions, ways to resolve methods and so on.
|
||||
|
||||
# Checking if an expr is calling a specific method
|
||||
|
||||
Starting with an `expr`, you can check whether it is calling a specific method `some_method`:
|
||||
|
||||
```rust
|
||||
impl LateLintPass<'_, '_> for MyStructLint {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
// Check our expr is calling a method
|
||||
if let hir::ExprKind::MethodCall(path, _, _args) = &expr.kind;
|
||||
// Check the name of this method is `some_method`
|
||||
if path.ident.name == sym!(some_method);
|
||||
then {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Checking if a type implements a specific trait
|
||||
|
||||
There are two ways to do this, depending if the target trait is part of lang items.
|
||||
@ -83,6 +105,32 @@ A list of defined paths for Clippy can be found in [paths.rs][paths]
|
||||
|
||||
We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate.
|
||||
|
||||
# Checking if a type defines a specific method
|
||||
|
||||
To check if our type defines a method called `some_method`:
|
||||
|
||||
```rust
|
||||
use crate::utils::{is_type_diagnostic_item, return_ty};
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MyTypeImpl {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx ImplItem<'_>) {
|
||||
if_chain! {
|
||||
// Check if item is a method/function
|
||||
if let ImplItemKind::Fn(ref signature, _) = impl_item.kind;
|
||||
// Check the method is named `some_method`
|
||||
if impl_item.ident.name == sym!(some_method);
|
||||
// We can also check it has a parameter `self`
|
||||
if signature.decl.implicit_self.has_implicit_self();
|
||||
// We can go further and even check if its return type is `String`
|
||||
if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type));
|
||||
then {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Dealing with macros
|
||||
|
||||
There are several helpers in Clippy's utils to deal with macros:
|
||||
|
@ -7,11 +7,11 @@ Clippy is released together with stable Rust releases. The dates for these
|
||||
releases can be found at the [Rust Forge]. This document explains the necessary
|
||||
steps to create a Clippy release.
|
||||
|
||||
1. [Find the Clippy commit](#find-the-clippy-commit)
|
||||
2. [Tag the stable commit](#tag-the-stable-commit)
|
||||
3. [Update `CHANGELOG.md`](#update-changelogmd)
|
||||
4. [Remerge the `beta` branch](#remerge-the-beta-branch)
|
||||
5. [Update the `beta` branch](#update-the-beta-branch)
|
||||
1. [Remerge the `beta` branch](#remerge-the-beta-branch)
|
||||
2. [Update the `beta` branch](#update-the-beta-branch)
|
||||
3. [Find the Clippy commit](#find-the-clippy-commit)
|
||||
4. [Tag the stable commit](#tag-the-stable-commit)
|
||||
5. [Update `CHANGELOG.md`](#update-changelogmd)
|
||||
|
||||
_NOTE: This document is for stable Rust releases, not for point releases. For
|
||||
point releases, step 1. and 2. should be enough._
|
||||
@ -19,6 +19,60 @@ point releases, step 1. and 2. should be enough._
|
||||
[Rust Forge]: https://forge.rust-lang.org/
|
||||
|
||||
|
||||
## Remerge the `beta` branch
|
||||
|
||||
This step is only necessary, if since the last release something was backported
|
||||
to the beta Rust release. The remerge is then necessary, to make sure that the
|
||||
Clippy commit, that was used by the now stable Rust release, persists in the
|
||||
tree of the Clippy repository.
|
||||
|
||||
To find out if this step is necessary run
|
||||
|
||||
```bash
|
||||
# Assumes that the local master branch is up-to-date
|
||||
$ git fetch upstream
|
||||
$ git branch master --contains upstream/beta
|
||||
```
|
||||
|
||||
If this command outputs `master`, this step is **not** necessary.
|
||||
|
||||
```bash
|
||||
# Assuming `HEAD` is the current `master` branch of rust-lang/rust-clippy
|
||||
$ git checkout -b backport_remerge
|
||||
$ git merge upstream/beta
|
||||
$ git diff # This diff has to be empty, otherwise something with the remerge failed
|
||||
$ git push origin backport_remerge # This can be pushed to your fork
|
||||
```
|
||||
|
||||
After this, open a PR to the master branch. In this PR, the commit hash of the
|
||||
`HEAD` of the `beta` branch must exists. In addition to that, no files should
|
||||
be changed by this PR.
|
||||
|
||||
|
||||
## Update the `beta` branch
|
||||
|
||||
This step must be done **after** the PR of the previous step was merged.
|
||||
|
||||
First, the Clippy commit of the `beta` branch of the Rust repository has to be
|
||||
determined.
|
||||
|
||||
```bash
|
||||
# Assuming the current directory corresponds to the Rust repository
|
||||
$ git checkout beta
|
||||
$ BETA_SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g")
|
||||
```
|
||||
|
||||
After finding the Clippy commit, the `beta` branch in the Clippy repository can
|
||||
be updated.
|
||||
|
||||
```bash
|
||||
# Assuming the current directory corresponds to the Clippy repository
|
||||
$ git checkout beta
|
||||
$ git rebase $BETA_SHA
|
||||
$ git push upstream beta
|
||||
```
|
||||
|
||||
|
||||
## Find the Clippy commit
|
||||
|
||||
The first step is to tag the Clippy commit, that is included in the stable Rust
|
||||
@ -28,8 +82,7 @@ release. This commit can be found in the Rust repository.
|
||||
# Assuming the current directory corresponds to the Rust repository
|
||||
$ git fetch upstream # `upstream` is the `rust-lang/rust` remote
|
||||
$ git checkout 1.XX.0 # XX should be exchanged with the corresponding version
|
||||
$ git submodule update
|
||||
$ SHA=$(git submodule status src/tools/clippy | awk '{print $1}')
|
||||
$ SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g")
|
||||
```
|
||||
|
||||
|
||||
@ -54,58 +107,3 @@ After this, the release should be available on the Clippy [release page].
|
||||
For this see the document on [how to update the changelog].
|
||||
|
||||
[how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md
|
||||
|
||||
|
||||
## Remerge the `beta` branch
|
||||
|
||||
This step is only necessary, if since the last release something was backported
|
||||
to the beta Rust release. The remerge is then necessary, to make sure that the
|
||||
Clippy commit, that was used by the now stable Rust release, persists in the
|
||||
tree of the Clippy repository.
|
||||
|
||||
To find out if this step is necessary run
|
||||
|
||||
```bash
|
||||
# Assumes that the local master branch is up-to-date
|
||||
$ git fetch upstream
|
||||
$ git branch master --contains upstream/beta
|
||||
```
|
||||
|
||||
If this command outputs `master`, this step is **not** necessary.
|
||||
|
||||
```bash
|
||||
# Assuming `HEAD` is the current `master` branch of rust-lang/rust-clippy
|
||||
$ git checkout -b backport_remerge
|
||||
$ git merge beta
|
||||
$ git diff # This diff has to be empty, otherwise something with the remerge failed
|
||||
$ git push origin backport_remerge # This can be pushed to your fork
|
||||
```
|
||||
|
||||
After this, open a PR to the master branch. In this PR, the commit hash of the
|
||||
`HEAD` of the `beta` branch must exists. In addition to that, no files should
|
||||
be changed by this PR.
|
||||
|
||||
|
||||
## Update the `beta` branch
|
||||
|
||||
This step must be done **after** the PR of the previous step was merged.
|
||||
|
||||
First, the Clippy commit of the `beta` branch of the Rust repository has to be
|
||||
determined.
|
||||
|
||||
```bash
|
||||
# Assuming the current directory corresponds to the Rust repository
|
||||
$ git checkout beta
|
||||
$ git submodule update
|
||||
$ BETA_SHA=$(git submodule status src/tools/clippy | awk '{print $1}')
|
||||
```
|
||||
|
||||
After finding the Clippy commit, the `beta` branch in the Clippy repository can
|
||||
be updated.
|
||||
|
||||
```bash
|
||||
# Assuming the current directory corresponds to the Clippy repository
|
||||
$ git checkout beta
|
||||
$ git rebase $BETA_SHA
|
||||
$ git push upstream beta
|
||||
```
|
||||
|
@ -166,7 +166,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
||||
},
|
||||
Lint {
|
||||
name: "cast_ptr_alignment",
|
||||
group: "correctness",
|
||||
group: "pedantic",
|
||||
desc: "cast from a pointer to a more-strictly-aligned pointer",
|
||||
deprecation: None,
|
||||
module: "types",
|
||||
@ -934,6 +934,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
||||
deprecation: None,
|
||||
module: "loops",
|
||||
},
|
||||
Lint {
|
||||
name: "iter_next_slice",
|
||||
group: "style",
|
||||
desc: "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`",
|
||||
deprecation: None,
|
||||
module: "methods",
|
||||
},
|
||||
Lint {
|
||||
name: "iter_nth",
|
||||
group: "perf",
|
||||
@ -1016,7 +1023,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
||||
group: "style",
|
||||
desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block",
|
||||
deprecation: None,
|
||||
module: "returns",
|
||||
module: "let_and_return",
|
||||
},
|
||||
Lint {
|
||||
name: "let_underscore_lock",
|
||||
@ -1735,7 +1742,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
||||
Lint {
|
||||
name: "pub_enum_variant_names",
|
||||
group: "pedantic",
|
||||
desc: "enums where all variants share a prefix/postfix",
|
||||
desc: "public enums where all variants share a prefix/postfix",
|
||||
deprecation: None,
|
||||
module: "enum_variants",
|
||||
},
|
||||
@ -2292,6 +2299,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
||||
deprecation: None,
|
||||
module: "no_effect",
|
||||
},
|
||||
Lint {
|
||||
name: "unnecessary_sort_by",
|
||||
group: "complexity",
|
||||
desc: "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer",
|
||||
deprecation: None,
|
||||
module: "unnecessary_sort_by",
|
||||
},
|
||||
Lint {
|
||||
name: "unnecessary_unwrap",
|
||||
group: "complexity",
|
||||
@ -2313,6 +2327,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
||||
deprecation: None,
|
||||
module: "misc_early",
|
||||
},
|
||||
Lint {
|
||||
name: "unnested_or_patterns",
|
||||
group: "complexity",
|
||||
desc: "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`",
|
||||
deprecation: None,
|
||||
module: "unnested_or_patterns",
|
||||
},
|
||||
Lint {
|
||||
name: "unreachable",
|
||||
group: "restriction",
|
||||
@ -2460,6 +2481,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
||||
deprecation: None,
|
||||
module: "types",
|
||||
},
|
||||
Lint {
|
||||
name: "vec_resize_to_zero",
|
||||
group: "correctness",
|
||||
desc: "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake",
|
||||
deprecation: None,
|
||||
module: "vec_resize_to_zero",
|
||||
},
|
||||
Lint {
|
||||
name: "verbose_bit_mask",
|
||||
group: "style",
|
||||
|
@ -184,8 +184,15 @@ fn run_ui_cargo(config: &mut compiletest::Config) {
|
||||
}
|
||||
|
||||
let src_path = case.path().join("src");
|
||||
env::set_current_dir(&src_path)?;
|
||||
|
||||
// When switching between branches, if the previous branch had a test
|
||||
// that the current branch does not have, the directory is not removed
|
||||
// because an ignored Cargo.lock file exists.
|
||||
if !src_path.exists() {
|
||||
continue;
|
||||
}
|
||||
|
||||
env::set_current_dir(&src_path)?;
|
||||
for file in fs::read_dir(&src_path)? {
|
||||
let file = file?;
|
||||
if file.file_type()?.is_dir() {
|
||||
|
@ -2,3 +2,5 @@
|
||||
name = "cargo_common_metadata"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
|
@ -9,3 +9,5 @@ readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
keywords = ["metadata", "lint", "clippy"]
|
||||
categories = ["development-tools::testing"]
|
||||
|
||||
[workspace]
|
||||
|
@ -5,6 +5,8 @@ name = "multiple_crate_versions"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
|
||||
# One of the versions of winapi is only a dev dependency: allowed
|
||||
[dependencies]
|
||||
ctrlc = "=3.1.0"
|
||||
|
@ -3,6 +3,8 @@ name = "multiple_crate_versions"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
ctrlc = "=3.1.0"
|
||||
ansi_term = "=0.11.0"
|
||||
|
@ -3,6 +3,8 @@ name = "cargo_common_metadata"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
regex = "1.3.7"
|
||||
serde = "1.0.110"
|
||||
|
@ -3,5 +3,7 @@ name = "wildcard_dependencies"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
regex = "*"
|
||||
|
@ -3,5 +3,7 @@ name = "wildcard_dependencies"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
regex = "1"
|
||||
|
@ -1,106 +1,76 @@
|
||||
// run-rustfix
|
||||
|
||||
#![allow(
|
||||
clippy::cast_lossless,
|
||||
// Int::max_value will be deprecated in the future
|
||||
deprecated,
|
||||
)]
|
||||
#![warn(clippy::checked_conversions)]
|
||||
#![allow(clippy::cast_lossless)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
// Positive tests
|
||||
|
||||
// Signed to unsigned
|
||||
|
||||
fn i64_to_u32(value: i64) -> Option<u32> {
|
||||
if u32::try_from(value).is_ok() {
|
||||
Some(value as u32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn i64_to_u32(value: i64) {
|
||||
let _ = u32::try_from(value).is_ok();
|
||||
let _ = u32::try_from(value).is_ok();
|
||||
}
|
||||
|
||||
fn i64_to_u16(value: i64) -> Option<u16> {
|
||||
if u16::try_from(value).is_ok() {
|
||||
Some(value as u16)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn i64_to_u16(value: i64) {
|
||||
let _ = u16::try_from(value).is_ok();
|
||||
let _ = u16::try_from(value).is_ok();
|
||||
}
|
||||
|
||||
fn isize_to_u8(value: isize) -> Option<u8> {
|
||||
if u8::try_from(value).is_ok() {
|
||||
Some(value as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn isize_to_u8(value: isize) {
|
||||
let _ = u8::try_from(value).is_ok();
|
||||
let _ = u8::try_from(value).is_ok();
|
||||
}
|
||||
|
||||
// Signed to signed
|
||||
|
||||
fn i64_to_i32(value: i64) -> Option<i32> {
|
||||
if i32::try_from(value).is_ok() {
|
||||
Some(value as i32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn i64_to_i32(value: i64) {
|
||||
let _ = i32::try_from(value).is_ok();
|
||||
let _ = i32::try_from(value).is_ok();
|
||||
}
|
||||
|
||||
fn i64_to_i16(value: i64) -> Option<i16> {
|
||||
if i16::try_from(value).is_ok() {
|
||||
Some(value as i16)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn i64_to_i16(value: i64) {
|
||||
let _ = i16::try_from(value).is_ok();
|
||||
let _ = i16::try_from(value).is_ok();
|
||||
}
|
||||
|
||||
// Unsigned to X
|
||||
|
||||
fn u32_to_i32(value: u32) -> Option<i32> {
|
||||
if i32::try_from(value).is_ok() {
|
||||
Some(value as i32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn u32_to_i32(value: u32) {
|
||||
let _ = i32::try_from(value).is_ok();
|
||||
let _ = i32::try_from(value).is_ok();
|
||||
}
|
||||
|
||||
fn usize_to_isize(value: usize) -> isize {
|
||||
if isize::try_from(value).is_ok() && value as i32 == 5 {
|
||||
5
|
||||
} else {
|
||||
1
|
||||
}
|
||||
pub fn usize_to_isize(value: usize) {
|
||||
let _ = isize::try_from(value).is_ok() && value as i32 == 5;
|
||||
let _ = isize::try_from(value).is_ok() && value as i32 == 5;
|
||||
}
|
||||
|
||||
fn u32_to_u16(value: u32) -> isize {
|
||||
if u16::try_from(value).is_ok() && value as i32 == 5 {
|
||||
5
|
||||
} else {
|
||||
1
|
||||
}
|
||||
pub fn u32_to_u16(value: u32) {
|
||||
let _ = u16::try_from(value).is_ok() && value as i32 == 5;
|
||||
let _ = u16::try_from(value).is_ok() && value as i32 == 5;
|
||||
}
|
||||
|
||||
// Negative tests
|
||||
|
||||
fn no_i64_to_i32(value: i64) -> Option<i32> {
|
||||
if value <= (i32::max_value() as i64) && value >= 0 {
|
||||
Some(value as i32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn no_i64_to_i32(value: i64) {
|
||||
let _ = value <= (i32::max_value() as i64) && value >= 0;
|
||||
let _ = value <= (i32::MAX as i64) && value >= 0;
|
||||
}
|
||||
|
||||
fn no_isize_to_u8(value: isize) -> Option<u8> {
|
||||
if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) {
|
||||
Some(value as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn no_isize_to_u8(value: isize) {
|
||||
let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize);
|
||||
let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize);
|
||||
}
|
||||
|
||||
fn i8_to_u8(value: i8) -> Option<u8> {
|
||||
if value >= 0 {
|
||||
Some(value as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn i8_to_u8(value: i8) {
|
||||
let _ = value >= 0;
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -1,106 +1,76 @@
|
||||
// run-rustfix
|
||||
|
||||
#![allow(
|
||||
clippy::cast_lossless,
|
||||
// Int::max_value will be deprecated in the future
|
||||
deprecated,
|
||||
)]
|
||||
#![warn(clippy::checked_conversions)]
|
||||
#![allow(clippy::cast_lossless)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
// Positive tests
|
||||
|
||||
// Signed to unsigned
|
||||
|
||||
fn i64_to_u32(value: i64) -> Option<u32> {
|
||||
if value <= (u32::max_value() as i64) && value >= 0 {
|
||||
Some(value as u32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn i64_to_u32(value: i64) {
|
||||
let _ = value <= (u32::max_value() as i64) && value >= 0;
|
||||
let _ = value <= (u32::MAX as i64) && value >= 0;
|
||||
}
|
||||
|
||||
fn i64_to_u16(value: i64) -> Option<u16> {
|
||||
if value <= i64::from(u16::max_value()) && value >= 0 {
|
||||
Some(value as u16)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn i64_to_u16(value: i64) {
|
||||
let _ = value <= i64::from(u16::max_value()) && value >= 0;
|
||||
let _ = value <= i64::from(u16::MAX) && value >= 0;
|
||||
}
|
||||
|
||||
fn isize_to_u8(value: isize) -> Option<u8> {
|
||||
if value <= (u8::max_value() as isize) && value >= 0 {
|
||||
Some(value as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn isize_to_u8(value: isize) {
|
||||
let _ = value <= (u8::max_value() as isize) && value >= 0;
|
||||
let _ = value <= (u8::MAX as isize) && value >= 0;
|
||||
}
|
||||
|
||||
// Signed to signed
|
||||
|
||||
fn i64_to_i32(value: i64) -> Option<i32> {
|
||||
if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) {
|
||||
Some(value as i32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn i64_to_i32(value: i64) {
|
||||
let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
|
||||
let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
|
||||
}
|
||||
|
||||
fn i64_to_i16(value: i64) -> Option<i16> {
|
||||
if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) {
|
||||
Some(value as i16)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn i64_to_i16(value: i64) {
|
||||
let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
|
||||
let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
|
||||
}
|
||||
|
||||
// Unsigned to X
|
||||
|
||||
fn u32_to_i32(value: u32) -> Option<i32> {
|
||||
if value <= i32::max_value() as u32 {
|
||||
Some(value as i32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn u32_to_i32(value: u32) {
|
||||
let _ = value <= i32::max_value() as u32;
|
||||
let _ = value <= i32::MAX as u32;
|
||||
}
|
||||
|
||||
fn usize_to_isize(value: usize) -> isize {
|
||||
if value <= isize::max_value() as usize && value as i32 == 5 {
|
||||
5
|
||||
} else {
|
||||
1
|
||||
}
|
||||
pub fn usize_to_isize(value: usize) {
|
||||
let _ = value <= isize::max_value() as usize && value as i32 == 5;
|
||||
let _ = value <= isize::MAX as usize && value as i32 == 5;
|
||||
}
|
||||
|
||||
fn u32_to_u16(value: u32) -> isize {
|
||||
if value <= u16::max_value() as u32 && value as i32 == 5 {
|
||||
5
|
||||
} else {
|
||||
1
|
||||
}
|
||||
pub fn u32_to_u16(value: u32) {
|
||||
let _ = value <= u16::max_value() as u32 && value as i32 == 5;
|
||||
let _ = value <= u16::MAX as u32 && value as i32 == 5;
|
||||
}
|
||||
|
||||
// Negative tests
|
||||
|
||||
fn no_i64_to_i32(value: i64) -> Option<i32> {
|
||||
if value <= (i32::max_value() as i64) && value >= 0 {
|
||||
Some(value as i32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn no_i64_to_i32(value: i64) {
|
||||
let _ = value <= (i32::max_value() as i64) && value >= 0;
|
||||
let _ = value <= (i32::MAX as i64) && value >= 0;
|
||||
}
|
||||
|
||||
fn no_isize_to_u8(value: isize) -> Option<u8> {
|
||||
if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) {
|
||||
Some(value as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn no_isize_to_u8(value: isize) {
|
||||
let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize);
|
||||
let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize);
|
||||
}
|
||||
|
||||
fn i8_to_u8(value: i8) -> Option<u8> {
|
||||
if value >= 0 {
|
||||
Some(value as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn i8_to_u8(value: i8) {
|
||||
let _ = value >= 0;
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -1,52 +1,100 @@
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:13:8
|
||||
--> $DIR/checked_conversions.rs:17:13
|
||||
|
|
||||
LL | if value <= (u32::max_value() as i64) && value >= 0 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
|
||||
LL | let _ = value <= (u32::max_value() as i64) && value >= 0;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
|
||||
|
|
||||
= note: `-D clippy::checked-conversions` implied by `-D warnings`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:21:8
|
||||
--> $DIR/checked_conversions.rs:18:13
|
||||
|
|
||||
LL | if value <= i64::from(u16::max_value()) && value >= 0 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
|
||||
LL | let _ = value <= (u32::MAX as i64) && value >= 0;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:29:8
|
||||
--> $DIR/checked_conversions.rs:22:13
|
||||
|
|
||||
LL | if value <= (u8::max_value() as isize) && value >= 0 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
|
||||
LL | let _ = value <= i64::from(u16::max_value()) && value >= 0;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:39:8
|
||||
--> $DIR/checked_conversions.rs:23:13
|
||||
|
|
||||
LL | if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
|
||||
LL | let _ = value <= i64::from(u16::MAX) && value >= 0;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:47:8
|
||||
--> $DIR/checked_conversions.rs:27:13
|
||||
|
|
||||
LL | if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
|
||||
LL | let _ = value <= (u8::max_value() as isize) && value >= 0;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:57:8
|
||||
--> $DIR/checked_conversions.rs:28:13
|
||||
|
|
||||
LL | if value <= i32::max_value() as u32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
|
||||
LL | let _ = value <= (u8::MAX as isize) && value >= 0;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:65:8
|
||||
--> $DIR/checked_conversions.rs:34:13
|
||||
|
|
||||
LL | if value <= isize::max_value() as usize && value as i32 == 5 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
|
||||
LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:73:8
|
||||
--> $DIR/checked_conversions.rs:35:13
|
||||
|
|
||||
LL | if value <= u16::max_value() as u32 && value as i32 == 5 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
|
||||
LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:39:13
|
||||
|
|
||||
LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:40:13
|
||||
|
|
||||
LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:46:13
|
||||
|
|
||||
LL | let _ = value <= i32::max_value() as u32;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:47:13
|
||||
|
|
||||
LL | let _ = value <= i32::MAX as u32;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:51:13
|
||||
|
|
||||
LL | let _ = value <= isize::max_value() as usize && value as i32 == 5;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:52:13
|
||||
|
|
||||
LL | let _ = value <= isize::MAX as usize && value as i32 == 5;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:56:13
|
||||
|
|
||||
LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
--> $DIR/checked_conversions.rs:57:13
|
||||
|
|
||||
LL | let _ = value <= u16::MAX as u32 && value as i32 == 5;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
|
||||
|
||||
error: aborting due to 16 previous errors
|
||||
|
||||
|
51
src/tools/clippy/tests/ui/crashes/ice-3969.rs
Normal file
51
src/tools/clippy/tests/ui/crashes/ice-3969.rs
Normal file
@ -0,0 +1,51 @@
|
||||
// https://github.com/rust-lang/rust-clippy/issues/3969
|
||||
// used to crash: error: internal compiler error:
|
||||
// src/librustc_traits/normalize_erasing_regions.rs:43: could not fully normalize `<i32 as
|
||||
// std::iter::Iterator>::Item test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs
|
||||
|
||||
// Check that tautalogically false bounds are accepted, and are used
|
||||
// in type inference.
|
||||
#![feature(trivial_bounds)]
|
||||
#![allow(unused)]
|
||||
|
||||
trait A {}
|
||||
|
||||
impl A for i32 {}
|
||||
|
||||
struct Dst<X: ?Sized> {
|
||||
x: X,
|
||||
}
|
||||
|
||||
struct TwoStrs(str, str)
|
||||
where
|
||||
str: Sized;
|
||||
|
||||
fn unsized_local()
|
||||
where
|
||||
for<'a> Dst<A + 'a>: Sized,
|
||||
{
|
||||
let x: Dst<A> = *(Box::new(Dst { x: 1 }) as Box<Dst<A>>);
|
||||
}
|
||||
|
||||
fn return_str() -> str
|
||||
where
|
||||
str: Sized,
|
||||
{
|
||||
*"Sized".to_string().into_boxed_str()
|
||||
}
|
||||
|
||||
fn use_op(s: String) -> String
|
||||
where
|
||||
String: ::std::ops::Neg<Output = String>,
|
||||
{
|
||||
-s
|
||||
}
|
||||
|
||||
fn use_for()
|
||||
where
|
||||
i32: Iterator,
|
||||
{
|
||||
for _ in 2i32 {}
|
||||
}
|
||||
|
||||
fn main() {}
|
22
src/tools/clippy/tests/ui/crashes/ice-3969.stderr
Normal file
22
src/tools/clippy/tests/ui/crashes/ice-3969.stderr
Normal file
@ -0,0 +1,22 @@
|
||||
error: trait objects without an explicit `dyn` are deprecated
|
||||
--> $DIR/ice-3969.rs:25:17
|
||||
|
|
||||
LL | for<'a> Dst<A + 'a>: Sized,
|
||||
| ^^^^^^ help: use `dyn`: `dyn A + 'a`
|
||||
|
|
||||
= note: `-D bare-trait-objects` implied by `-D warnings`
|
||||
|
||||
error: trait objects without an explicit `dyn` are deprecated
|
||||
--> $DIR/ice-3969.rs:27:16
|
||||
|
|
||||
LL | let x: Dst<A> = *(Box::new(Dst { x: 1 }) as Box<Dst<A>>);
|
||||
| ^ help: use `dyn`: `dyn A`
|
||||
|
||||
error: trait objects without an explicit `dyn` are deprecated
|
||||
--> $DIR/ice-3969.rs:27:57
|
||||
|
|
||||
LL | let x: Dst<A> = *(Box::new(Dst { x: 1 }) as Box<Dst<A>>);
|
||||
| ^ help: use `dyn`: `dyn A`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
@ -6,4 +6,8 @@ pub fn foo(bar: *const u8) {
|
||||
println!("{:#p}", bar);
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/rust-lang/rust-clippy/issues/4917
|
||||
/// <foo
|
||||
struct A {}
|
||||
|
||||
fn main() {}
|
||||
|
@ -40,4 +40,6 @@ fn main() {
|
||||
let _ = (&HashSet::<i32>::new()).iter(); //~ WARN equivalent to .iter()
|
||||
let _ = std::path::Path::new("12/34").iter(); //~ WARN equivalent to .iter()
|
||||
let _ = std::path::PathBuf::from("12/34").iter(); //~ ERROR equivalent to .iter()
|
||||
|
||||
let _ = (&[1, 2, 3]).iter().next(); //~ WARN equivalent to .iter()
|
||||
}
|
||||
|
@ -40,4 +40,6 @@ fn main() {
|
||||
let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
|
||||
let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter()
|
||||
let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
|
||||
|
||||
let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter()
|
||||
}
|
||||
|
@ -156,5 +156,11 @@ error: this `.into_iter()` call is equivalent to `.iter()` and will not move the
|
||||
LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
|
||||
| ^^^^^^^^^ help: call directly: `iter`
|
||||
|
||||
error: aborting due to 26 previous errors
|
||||
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array`
|
||||
--> $DIR/into_iter_on_ref.rs:44:26
|
||||
|
|
||||
LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter()
|
||||
| ^^^^^^^^^ help: call directly: `iter`
|
||||
|
||||
error: aborting due to 27 previous errors
|
||||
|
||||
|
24
src/tools/clippy/tests/ui/iter_next_slice.fixed
Normal file
24
src/tools/clippy/tests/ui/iter_next_slice.fixed
Normal file
@ -0,0 +1,24 @@
|
||||
// run-rustfix
|
||||
#![warn(clippy::iter_next_slice)]
|
||||
|
||||
fn main() {
|
||||
// test code goes here
|
||||
let s = [1, 2, 3];
|
||||
let v = vec![1, 2, 3];
|
||||
|
||||
s.get(0);
|
||||
// Should be replaced by s.get(0)
|
||||
|
||||
s.get(2);
|
||||
// Should be replaced by s.get(2)
|
||||
|
||||
v.get(5);
|
||||
// Should be replaced by v.get(5)
|
||||
|
||||
v.get(0);
|
||||
// Should be replaced by v.get(0)
|
||||
|
||||
let o = Some(5);
|
||||
o.iter().next();
|
||||
// Shouldn't be linted since this is not a Slice or an Array
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user