mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-26 14:43:24 +00:00
Auto merge of #97730 - flip1995:clippyup, r=Manishearth
Update Clippy r? `@Manishearth` This includes a bit bigger `Cargo.lock` update.
This commit is contained in:
commit
50b00252ae
119
Cargo.lock
119
Cargo.lock
@ -354,7 +354,7 @@ dependencies = [
|
||||
"percent-encoding 2.1.0",
|
||||
"pretty_env_logger",
|
||||
"rustc-workspace-hack",
|
||||
"rustfix 0.6.0",
|
||||
"rustfix",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_ignored",
|
||||
@ -638,7 +638,7 @@ dependencies = [
|
||||
"futures 0.3.19",
|
||||
"if_chain",
|
||||
"itertools",
|
||||
"parking_lot",
|
||||
"parking_lot 0.12.1",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-semver",
|
||||
@ -658,7 +658,7 @@ name = "clippy_dev"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"clap 2.34.0",
|
||||
"clap 3.1.1",
|
||||
"indoc",
|
||||
"itertools",
|
||||
"opener",
|
||||
@ -767,7 +767,7 @@ dependencies = [
|
||||
"libc",
|
||||
"miow",
|
||||
"regex",
|
||||
"rustfix 0.6.0",
|
||||
"rustfix",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tracing",
|
||||
@ -779,9 +779,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "compiletest_rs"
|
||||
version = "0.7.1"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29843cb8d351febf86557681d049d1e1652b81a086a190fa1173c07fd17fbf83"
|
||||
checksum = "262134ef87408da1ddfe45e33daa0ca43b75286d6b1076446e602d264cf9847e"
|
||||
dependencies = [
|
||||
"diff",
|
||||
"filetime",
|
||||
@ -791,7 +791,7 @@ dependencies = [
|
||||
"log",
|
||||
"miow",
|
||||
"regex",
|
||||
"rustfix 0.5.1",
|
||||
"rustfix",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@ -2041,7 +2041,7 @@ dependencies = [
|
||||
"jsonrpc-server-utils",
|
||||
"log",
|
||||
"parity-tokio-ipc",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
@ -2055,7 +2055,7 @@ dependencies = [
|
||||
"jsonrpc-core",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
]
|
||||
@ -2203,10 +2203,11 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.5"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
|
||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
@ -2341,7 +2342,7 @@ checksum = "78f7a41bc6f856a2cf0e95094ad5121f82500e2d9a0f3c0171d98f6566d8117d"
|
||||
dependencies = [
|
||||
"log",
|
||||
"memmap2",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"perf-event-open-sys",
|
||||
"rustc-hash",
|
||||
"smallvec",
|
||||
@ -2355,7 +2356,7 @@ checksum = "bd460fad6e55ca82fa0cd9dab0d315294188fd9ec6efbf4105e5635d4872ef9c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"memmap2",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"perf-event-open-sys",
|
||||
"rustc-hash",
|
||||
"smallvec",
|
||||
@ -2704,7 +2705,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
"parking_lot_core 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2721,6 +2732,19 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.1"
|
||||
@ -3732,7 +3756,7 @@ dependencies = [
|
||||
"libc",
|
||||
"measureme 10.0.0",
|
||||
"memmap2",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"rustc-hash",
|
||||
"rustc-rayon",
|
||||
"rustc-rayon-core",
|
||||
@ -4316,7 +4340,7 @@ dependencies = [
|
||||
name = "rustc_query_system"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"rustc-rayon-core",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
@ -4619,21 +4643,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustfix"
|
||||
version = "0.5.1"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2c50b74badcddeb8f7652fa8323ce440b95286f8e4b64ebfd871c609672704e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustfix"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f0be05fc0675ef4f47119dc39cfc46636bb77d4fc4ef1bd851b9c3f7697f32a"
|
||||
checksum = "ecd2853d9e26988467753bd9912c3a126f642d05d229a4b53f5752ee36c56481"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"log",
|
||||
@ -5023,7 +5035,7 @@ checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"new_debug_unreachable",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"phf_shared",
|
||||
"precomputed-hash",
|
||||
"serde",
|
||||
@ -5467,7 +5479,7 @@ dependencies = [
|
||||
"ansi_term",
|
||||
"lazy_static",
|
||||
"matchers",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"regex",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
@ -5869,6 +5881,49 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "0.2.2"
|
||||
|
@ -3276,9 +3276,11 @@ Released 2018-09-13
|
||||
<!-- begin autogenerated links to lint list -->
|
||||
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
|
||||
[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
|
||||
[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
|
||||
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
|
||||
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
|
||||
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
|
||||
[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
|
||||
[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
|
||||
[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
|
||||
[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
|
||||
@ -3296,6 +3298,7 @@ Released 2018-09-13
|
||||
[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
|
||||
[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
|
||||
[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr
|
||||
[`borrow_deref_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref
|
||||
[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
|
||||
[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
|
||||
[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
|
||||
@ -3362,6 +3365,7 @@ Released 2018-09-13
|
||||
[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
|
||||
[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
|
||||
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
|
||||
[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
|
||||
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
|
||||
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
|
||||
[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
|
||||
@ -3435,6 +3439,7 @@ Released 2018-09-13
|
||||
[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
|
||||
[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
|
||||
[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
|
||||
[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first
|
||||
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
|
||||
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
|
||||
[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
|
||||
@ -3556,6 +3561,7 @@ Released 2018-09-13
|
||||
[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
|
||||
[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
|
||||
[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
|
||||
[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
|
||||
[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
|
||||
[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
|
||||
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
|
||||
@ -3613,6 +3619,7 @@ Released 2018-09-13
|
||||
[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
|
||||
[`new_without_default_derive`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default_derive
|
||||
[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
|
||||
[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace
|
||||
[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
|
||||
[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
|
||||
[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
|
||||
@ -3729,7 +3736,6 @@ Released 2018-09-13
|
||||
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
|
||||
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
|
||||
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
|
||||
[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee
|
||||
[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
|
||||
[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
|
||||
[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
|
||||
@ -3750,6 +3756,7 @@ Released 2018-09-13
|
||||
[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
|
||||
[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
|
||||
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
|
||||
[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
|
||||
[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
|
||||
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
|
||||
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
|
||||
@ -3820,6 +3827,7 @@ Released 2018-09-13
|
||||
[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
|
||||
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
|
||||
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
|
||||
[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
|
||||
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
|
||||
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
|
||||
[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
|
||||
|
@ -28,7 +28,7 @@ tempfile = { version = "3.2", optional = true }
|
||||
termize = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
compiletest_rs = { version = "0.7.1", features = ["tmp"] }
|
||||
compiletest_rs = { version = "0.8", features = ["tmp"] }
|
||||
tester = "0.9"
|
||||
regex = "1.5"
|
||||
# This is used by the `collect-metadata` alias.
|
||||
@ -48,7 +48,7 @@ quote = "1.0"
|
||||
serde = { version = "1.0.125", features = ["derive"] }
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
futures = "0.3"
|
||||
parking_lot = "0.11.2"
|
||||
parking_lot = "0.12"
|
||||
tokio = { version = "1", features = ["io-util"] }
|
||||
rustc-semver = "1.1"
|
||||
|
||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
aho-corasick = "0.7"
|
||||
clap = "2.33"
|
||||
clap = "3.1"
|
||||
indoc = "1.0"
|
||||
itertools = "0.10.1"
|
||||
opener = "0.5"
|
||||
|
@ -2,20 +2,20 @@
|
||||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
|
||||
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
|
||||
use indoc::indoc;
|
||||
fn main() {
|
||||
let matches = get_clap_config();
|
||||
|
||||
match matches.subcommand() {
|
||||
("bless", Some(matches)) => {
|
||||
Some(("bless", matches)) => {
|
||||
bless::bless(matches.is_present("ignore-timestamp"));
|
||||
},
|
||||
("fmt", Some(matches)) => {
|
||||
Some(("fmt", matches)) => {
|
||||
fmt::run(matches.is_present("check"), matches.is_present("verbose"));
|
||||
},
|
||||
("update_lints", Some(matches)) => {
|
||||
Some(("update_lints", matches)) => {
|
||||
if matches.is_present("print-only") {
|
||||
update_lints::print_lints();
|
||||
} else if matches.is_present("check") {
|
||||
@ -24,7 +24,7 @@ fn main() {
|
||||
update_lints::update(update_lints::UpdateMode::Change);
|
||||
}
|
||||
},
|
||||
("new_lint", Some(matches)) => {
|
||||
Some(("new_lint", matches)) => {
|
||||
match new_lint::create(
|
||||
matches.value_of("pass"),
|
||||
matches.value_of("name"),
|
||||
@ -35,8 +35,8 @@ fn main() {
|
||||
Err(e) => eprintln!("Unable to create lint: {}", e),
|
||||
}
|
||||
},
|
||||
("setup", Some(sub_command)) => match sub_command.subcommand() {
|
||||
("intellij", Some(matches)) => {
|
||||
Some(("setup", sub_command)) => match sub_command.subcommand() {
|
||||
Some(("intellij", matches)) => {
|
||||
if matches.is_present("remove") {
|
||||
setup::intellij::remove_rustc_src();
|
||||
} else {
|
||||
@ -47,14 +47,14 @@ fn main() {
|
||||
);
|
||||
}
|
||||
},
|
||||
("git-hook", Some(matches)) => {
|
||||
Some(("git-hook", matches)) => {
|
||||
if matches.is_present("remove") {
|
||||
setup::git_hook::remove_hook();
|
||||
} else {
|
||||
setup::git_hook::install_hook(matches.is_present("force-override"));
|
||||
}
|
||||
},
|
||||
("vscode-tasks", Some(matches)) => {
|
||||
Some(("vscode-tasks", matches)) => {
|
||||
if matches.is_present("remove") {
|
||||
setup::vscode::remove_tasks();
|
||||
} else {
|
||||
@ -63,23 +63,23 @@ fn main() {
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
("remove", Some(sub_command)) => match sub_command.subcommand() {
|
||||
("git-hook", Some(_)) => setup::git_hook::remove_hook(),
|
||||
("intellij", Some(_)) => setup::intellij::remove_rustc_src(),
|
||||
("vscode-tasks", Some(_)) => setup::vscode::remove_tasks(),
|
||||
Some(("remove", sub_command)) => match sub_command.subcommand() {
|
||||
Some(("git-hook", _)) => setup::git_hook::remove_hook(),
|
||||
Some(("intellij", _)) => setup::intellij::remove_rustc_src(),
|
||||
Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(),
|
||||
_ => {},
|
||||
},
|
||||
("serve", Some(matches)) => {
|
||||
Some(("serve", matches)) => {
|
||||
let port = matches.value_of("port").unwrap().parse().unwrap();
|
||||
let lint = matches.value_of("lint");
|
||||
serve::run(port, lint);
|
||||
},
|
||||
("lint", Some(matches)) => {
|
||||
Some(("lint", matches)) => {
|
||||
let path = matches.value_of("path").unwrap();
|
||||
let args = matches.values_of("args").into_iter().flatten();
|
||||
lint::run(path, args);
|
||||
},
|
||||
("rename_lint", Some(matches)) => {
|
||||
Some(("rename_lint", matches)) => {
|
||||
let old_name = matches.value_of("old_name").unwrap();
|
||||
let new_name = matches.value_of("new_name").unwrap_or(old_name);
|
||||
let uplift = matches.is_present("uplift");
|
||||
@ -89,35 +89,24 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
App::new("Clippy developer tooling")
|
||||
.setting(AppSettings::ArgRequiredElseHelp)
|
||||
fn get_clap_config() -> ArgMatches {
|
||||
Command::new("Clippy developer tooling")
|
||||
.arg_required_else_help(true)
|
||||
.subcommand(
|
||||
SubCommand::with_name("bless")
|
||||
.about("bless the test output changes")
|
||||
.arg(
|
||||
Arg::with_name("ignore-timestamp")
|
||||
.long("ignore-timestamp")
|
||||
.help("Include files updated before clippy was built"),
|
||||
),
|
||||
Command::new("bless").about("bless the test output changes").arg(
|
||||
Arg::new("ignore-timestamp")
|
||||
.long("ignore-timestamp")
|
||||
.help("Include files updated before clippy was built"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("fmt")
|
||||
Command::new("fmt")
|
||||
.about("Run rustfmt on all projects and tests")
|
||||
.arg(
|
||||
Arg::with_name("check")
|
||||
.long("check")
|
||||
.help("Use the rustfmt --check option"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("verbose")
|
||||
.short("v")
|
||||
.long("verbose")
|
||||
.help("Echo commands run"),
|
||||
),
|
||||
.arg(Arg::new("check").long("check").help("Use the rustfmt --check option"))
|
||||
.arg(Arg::new("verbose").short('v').long("verbose").help("Echo commands run")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("update_lints")
|
||||
Command::new("update_lints")
|
||||
.about("Updates lint registration and information from the source code")
|
||||
.long_about(
|
||||
"Makes sure that:\n \
|
||||
@ -127,23 +116,23 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
* lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
|
||||
* all lints are registered in the lint store",
|
||||
)
|
||||
.arg(Arg::with_name("print-only").long("print-only").help(
|
||||
.arg(Arg::new("print-only").long("print-only").help(
|
||||
"Print a table of lints to STDOUT. \
|
||||
This does not include deprecated and internal lints. \
|
||||
(Does not modify any files)",
|
||||
))
|
||||
.arg(
|
||||
Arg::with_name("check")
|
||||
Arg::new("check")
|
||||
.long("check")
|
||||
.help("Checks that `cargo dev update_lints` has been run. Used on CI."),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("new_lint")
|
||||
Command::new("new_lint")
|
||||
.about("Create new lint and run `cargo dev update_lints`")
|
||||
.arg(
|
||||
Arg::with_name("pass")
|
||||
.short("p")
|
||||
Arg::new("pass")
|
||||
.short('p')
|
||||
.long("pass")
|
||||
.help("Specify whether the lint runs during the early or late pass")
|
||||
.takes_value(true)
|
||||
@ -151,16 +140,16 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("name")
|
||||
.short("n")
|
||||
Arg::new("name")
|
||||
.short('n')
|
||||
.long("name")
|
||||
.help("Name of the new lint in snake case, ex: fn_too_long")
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("category")
|
||||
.short("c")
|
||||
Arg::new("category")
|
||||
.short('c')
|
||||
.long("category")
|
||||
.help("What category the lint belongs to")
|
||||
.default_value("nursery")
|
||||
@ -179,29 +168,25 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
])
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("msrv")
|
||||
.long("msrv")
|
||||
.help("Add MSRV config code to the lint"),
|
||||
),
|
||||
.arg(Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("setup")
|
||||
Command::new("setup")
|
||||
.about("Support for setting up your personal development environment")
|
||||
.setting(AppSettings::ArgRequiredElseHelp)
|
||||
.arg_required_else_help(true)
|
||||
.subcommand(
|
||||
SubCommand::with_name("intellij")
|
||||
Command::new("intellij")
|
||||
.about("Alter dependencies so Intellij Rust can find rustc internals")
|
||||
.arg(
|
||||
Arg::with_name("remove")
|
||||
Arg::new("remove")
|
||||
.long("remove")
|
||||
.help("Remove the dependencies added with 'cargo dev setup intellij'")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("rustc-repo-path")
|
||||
Arg::new("rustc-repo-path")
|
||||
.long("repo-path")
|
||||
.short("r")
|
||||
.short('r')
|
||||
.help("The path to a rustc repo that will be used for setting the dependencies")
|
||||
.takes_value(true)
|
||||
.value_name("path")
|
||||
@ -210,66 +195,65 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("git-hook")
|
||||
Command::new("git-hook")
|
||||
.about("Add a pre-commit git hook that formats your code to make it look pretty")
|
||||
.arg(
|
||||
Arg::with_name("remove")
|
||||
Arg::new("remove")
|
||||
.long("remove")
|
||||
.help("Remove the pre-commit hook added with 'cargo dev setup git-hook'")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("force-override")
|
||||
Arg::new("force-override")
|
||||
.long("force-override")
|
||||
.short("f")
|
||||
.short('f')
|
||||
.help("Forces the override of an existing git pre-commit hook")
|
||||
.required(false),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vscode-tasks")
|
||||
Command::new("vscode-tasks")
|
||||
.about("Add several tasks to vscode for formatting, validation and testing")
|
||||
.arg(
|
||||
Arg::with_name("remove")
|
||||
Arg::new("remove")
|
||||
.long("remove")
|
||||
.help("Remove the tasks added with 'cargo dev setup vscode-tasks'")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("force-override")
|
||||
Arg::new("force-override")
|
||||
.long("force-override")
|
||||
.short("f")
|
||||
.short('f')
|
||||
.help("Forces the override of existing vscode tasks")
|
||||
.required(false),
|
||||
),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("remove")
|
||||
Command::new("remove")
|
||||
.about("Support for undoing changes done by the setup command")
|
||||
.setting(AppSettings::ArgRequiredElseHelp)
|
||||
.subcommand(SubCommand::with_name("git-hook").about("Remove any existing pre-commit git hook"))
|
||||
.subcommand(SubCommand::with_name("vscode-tasks").about("Remove any existing vscode tasks"))
|
||||
.arg_required_else_help(true)
|
||||
.subcommand(Command::new("git-hook").about("Remove any existing pre-commit git hook"))
|
||||
.subcommand(Command::new("vscode-tasks").about("Remove any existing vscode tasks"))
|
||||
.subcommand(
|
||||
SubCommand::with_name("intellij")
|
||||
.about("Removes rustc source paths added via `cargo dev setup intellij`"),
|
||||
Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("serve")
|
||||
Command::new("serve")
|
||||
.about("Launch a local 'ALL the Clippy Lints' website in a browser")
|
||||
.arg(
|
||||
Arg::with_name("port")
|
||||
Arg::new("port")
|
||||
.long("port")
|
||||
.short("p")
|
||||
.short('p')
|
||||
.help("Local port for the http server")
|
||||
.default_value("8000")
|
||||
.validator_os(serve::validate_port),
|
||||
)
|
||||
.arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
|
||||
.arg(Arg::new("lint").help("Which lint's page to load initially (optional)")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("lint")
|
||||
Command::new("lint")
|
||||
.about("Manually run clippy on a file or package")
|
||||
.after_help(indoc! {"
|
||||
EXAMPLES
|
||||
@ -288,33 +272,33 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
cargo dev lint ~/my-project -- -- -W clippy::pedantic
|
||||
"})
|
||||
.arg(
|
||||
Arg::with_name("path")
|
||||
Arg::new("path")
|
||||
.required(true)
|
||||
.help("The path to a file or package directory to lint"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("args")
|
||||
.multiple(true)
|
||||
Arg::new("args")
|
||||
.multiple_occurrences(true)
|
||||
.help("Pass extra arguments to cargo/clippy-driver"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("rename_lint")
|
||||
Command::new("rename_lint")
|
||||
.about("Renames the given lint")
|
||||
.arg(
|
||||
Arg::with_name("old_name")
|
||||
Arg::new("old_name")
|
||||
.index(1)
|
||||
.required(true)
|
||||
.help("The name of the lint to rename"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("new_name")
|
||||
Arg::new("new_name")
|
||||
.index(2)
|
||||
.required_unless("uplift")
|
||||
.required_unless_present("uplift")
|
||||
.help("The new name of the lint"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("uplift")
|
||||
Arg::new("uplift")
|
||||
.long("uplift")
|
||||
.help("This lint will be uplifted into rustc"),
|
||||
),
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::ffi::OsStr;
|
||||
use std::num::ParseIntError;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::thread;
|
||||
@ -59,9 +60,6 @@ fn mtime(path: impl AsRef<Path>) -> SystemTime {
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_errors_doc)]
|
||||
pub fn validate_port(arg: &OsStr) -> Result<(), OsString> {
|
||||
match arg.to_string_lossy().parse::<u16>() {
|
||||
Ok(_port) => Ok(()),
|
||||
Err(err) => Err(OsString::from(err.to_string())),
|
||||
}
|
||||
pub fn validate_port(arg: &OsStr) -> Result<(), ParseIntError> {
|
||||
arg.to_string_lossy().parse::<u16>().map(|_| ())
|
||||
}
|
||||
|
@ -0,0 +1,100 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{trim_span, walk_span_to_context};
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for ranges which almost include the entire range of letters from 'a' to 'z', but
|
||||
/// don't because they're a half open range.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This (`'a'..'z'`) is almost certainly a typo meant to include all letters.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let _ = 'a'..'z';
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let _ = 'a'..='z';
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub ALMOST_COMPLETE_LETTER_RANGE,
|
||||
suspicious,
|
||||
"almost complete letter range"
|
||||
}
|
||||
impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]);
|
||||
|
||||
pub struct AlmostCompleteLetterRange {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
impl AlmostCompleteLetterRange {
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
impl EarlyLintPass for AlmostCompleteLetterRange {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
|
||||
if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind {
|
||||
let ctxt = e.span.ctxt();
|
||||
let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt)
|
||||
&& let Some(end) = walk_span_to_context(end.span, ctxt)
|
||||
&& meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE)
|
||||
{
|
||||
Some((trim_span(cx.sess().source_map(), start.between(end)), "..="))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
check_range(cx, e.span, start, end, sugg);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) {
|
||||
if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
|
||||
&& matches!(kind.node, RangeEnd::Excluded)
|
||||
{
|
||||
let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) {
|
||||
"..="
|
||||
} else {
|
||||
"..."
|
||||
};
|
||||
check_range(cx, p.span, start, end, Some((kind.span, sugg)));
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(EarlyContext);
|
||||
}
|
||||
|
||||
fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) {
|
||||
if let ExprKind::Lit(start_lit) = &start.peel_parens().kind
|
||||
&& let ExprKind::Lit(end_lit) = &end.peel_parens().kind
|
||||
&& matches!(
|
||||
(&start_lit.kind, &end_lit.kind),
|
||||
(LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z'))
|
||||
| (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z'))
|
||||
)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
ALMOST_COMPLETE_LETTER_RANGE,
|
||||
span,
|
||||
"almost complete ascii letter range",
|
||||
|diag| {
|
||||
if let Some((span, sugg)) = sugg {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"use an inclusive range",
|
||||
sugg.to_owned(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ declare_clippy_lint! {
|
||||
/// let x = 3.14;
|
||||
/// let y = 1_f64 / x;
|
||||
/// ```
|
||||
/// Use predefined constants instead:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = std::f32::consts::PI;
|
||||
/// let y = std::f64::consts::FRAC_1_PI;
|
||||
|
@ -29,15 +29,14 @@ declare_clippy_lint! {
|
||||
/// f(a as u16);
|
||||
/// ```
|
||||
///
|
||||
/// Usually better represents the semantics you expect:
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// f(a.try_into()?);
|
||||
/// ```
|
||||
/// or
|
||||
/// ```rust,ignore
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// f(a.try_into().expect("Unexpected u16 overflow in f"));
|
||||
/// ```
|
||||
///
|
||||
#[clippy::version = "1.41.0"]
|
||||
pub AS_CONVERSIONS,
|
||||
restriction,
|
||||
|
74
src/tools/clippy/clippy_lints/src/as_underscore.rs
Normal file
74
src/tools/clippy/clippy_lints/src/as_underscore.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for the usage of `as _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The conversion might include lossy conversion and dangerous cast that might go
|
||||
/// undetected du to the type being inferred.
|
||||
///
|
||||
/// The lint is allowed by default as using `_` is less wordy than always specifying the type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as _);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as usize);
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub AS_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as _` conversion"
|
||||
}
|
||||
declare_lint_pass!(AsUnderscore => [AS_UNDERSCORE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for AsUnderscore {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Cast(_, ty) = expr.kind && let TyKind::Infer = ty.kind {
|
||||
|
||||
let ty_resolved = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Error(_) = ty_resolved.kind() {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
AS_UNDERSCORE,
|
||||
expr.span,
|
||||
"using `as _` conversion",
|
||||
None,
|
||||
"consider giving the type explicitly",
|
||||
);
|
||||
} else {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
AS_UNDERSCORE,
|
||||
expr.span,
|
||||
"using `as _` conversion",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
ty.span,
|
||||
"consider giving the type explicitly",
|
||||
format!("{}", ty_resolved),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -27,10 +27,16 @@ declare_clippy_lint! {
|
||||
/// let mut a = 5;
|
||||
/// let b = 0;
|
||||
/// // ...
|
||||
/// // Bad
|
||||
/// a = a + b;
|
||||
///
|
||||
/// // Good
|
||||
/// a = a + b;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let mut a = 5;
|
||||
/// let b = 0;
|
||||
/// // ...
|
||||
///
|
||||
/// a += b;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
@ -89,13 +89,14 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// // Bad
|
||||
/// #[deny(dead_code)]
|
||||
/// extern crate foo;
|
||||
/// #[forbid(dead_code)]
|
||||
/// use foo::bar;
|
||||
/// ```
|
||||
///
|
||||
/// // Ok
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// #[allow(unused_imports)]
|
||||
/// use foo::baz;
|
||||
/// #[allow(unused_imports)]
|
||||
@ -146,15 +147,19 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// #[allow(dead_code)]
|
||||
///
|
||||
/// fn not_quite_good_code() { }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // Good (as inner attribute)
|
||||
/// #![allow(dead_code)]
|
||||
///
|
||||
/// fn this_is_fine() { }
|
||||
///
|
||||
/// // Bad
|
||||
/// #[allow(dead_code)]
|
||||
///
|
||||
/// fn not_quite_good_code() { }
|
||||
/// // or
|
||||
///
|
||||
/// // Good (as outer attribute)
|
||||
/// #[allow(dead_code)]
|
||||
@ -175,12 +180,11 @@ declare_clippy_lint! {
|
||||
/// These lints should only be enabled on a lint-by-lint basis and with careful consideration.
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// #![deny(clippy::restriction)]
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// #![deny(clippy::as_conversions)]
|
||||
/// ```
|
||||
@ -205,13 +209,12 @@ declare_clippy_lint! {
|
||||
/// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// #[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
/// fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// #[rustfmt::skip]
|
||||
/// fn main() { }
|
||||
@ -231,20 +234,20 @@ declare_clippy_lint! {
|
||||
/// by the conditional compilation engine.
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// #[cfg(linux)]
|
||||
/// fn conditional() { }
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # mod hidden {
|
||||
/// #[cfg(target_os = "linux")]
|
||||
/// fn conditional() { }
|
||||
/// ```
|
||||
/// # }
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// Or:
|
||||
/// ```rust
|
||||
/// #[cfg(unix)]
|
||||
/// fn conditional() { }
|
||||
/// ```
|
||||
@ -266,14 +269,13 @@ declare_clippy_lint! {
|
||||
/// ensure that others understand the reasoning
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// #![feature(lint_reasons)]
|
||||
///
|
||||
/// #![allow(clippy::some_lint)]
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// #![feature(lint_reasons)]
|
||||
///
|
||||
@ -585,15 +587,21 @@ impl EarlyLintPass for EarlyAttributes {
|
||||
}
|
||||
|
||||
fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
|
||||
for attr in &item.attrs {
|
||||
let mut iter = item.attrs.iter().peekable();
|
||||
while let Some(attr) = iter.next() {
|
||||
if matches!(attr.kind, AttrKind::Normal(..))
|
||||
&& attr.style == AttrStyle::Outer
|
||||
&& is_present_in_source(cx, attr.span)
|
||||
{
|
||||
let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent());
|
||||
let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt(), item.span.parent());
|
||||
let end_of_attr_to_next_attr_or_item = Span::new(
|
||||
attr.span.hi(),
|
||||
iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()),
|
||||
item.span.ctxt(),
|
||||
item.span.parent(),
|
||||
);
|
||||
|
||||
if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
|
||||
if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) {
|
||||
let lines = snippet.split('\n').collect::<Vec<_>>();
|
||||
let lines = without_block_comments(lines);
|
||||
|
||||
@ -623,8 +631,15 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Opti
|
||||
if feature_item.has_name(sym::rustfmt);
|
||||
// check for `rustfmt_skip` and `rustfmt::skip`
|
||||
if let Some(skip_item) = &items[1].meta_item();
|
||||
if skip_item.has_name(sym!(rustfmt_skip)) ||
|
||||
skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip;
|
||||
if skip_item.has_name(sym!(rustfmt_skip))
|
||||
|| skip_item
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.expect("empty path in attribute")
|
||||
.ident
|
||||
.name
|
||||
== sym::skip;
|
||||
// Only lint outer attributes, because custom inner attributes are unstable
|
||||
// Tracking issue: https://github.com/rust-lang/rust/issues/54726
|
||||
if attr.style == AttrStyle::Outer;
|
||||
|
@ -22,21 +22,17 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// # fn somefunc() -> bool { true };
|
||||
/// if { true } { /* ... */ }
|
||||
///
|
||||
/// // Good
|
||||
/// if true { /* ... */ }
|
||||
/// if { let x = somefunc(); x } { /* ... */ }
|
||||
/// ```
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # fn somefunc() -> bool { true };
|
||||
/// // Bad
|
||||
/// if { let x = somefunc(); x } { /* ... */ }
|
||||
/// if true { /* ... */ }
|
||||
///
|
||||
/// // Good
|
||||
/// let res = { let x = somefunc(); x };
|
||||
/// if res { /* ... */ }
|
||||
/// ```
|
||||
|
@ -27,8 +27,14 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// if a && true // should be: if a
|
||||
/// if !(a == b) // should be: if a != b
|
||||
/// if a && true {}
|
||||
/// if !(a == b) {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// if a {}
|
||||
/// if a != b {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NONMINIMAL_BOOL,
|
||||
@ -48,10 +54,15 @@ declare_clippy_lint! {
|
||||
/// Ignores short circuiting behavior.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// ```rust,ignore
|
||||
/// // The `b` is unnecessary, the expression is equivalent to `if a`.
|
||||
/// if a && b || a { ... }
|
||||
/// ```
|
||||
/// The `b` is unnecessary, the expression is equivalent to `if a`.
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// if a {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub LOGIC_BUG,
|
||||
correctness,
|
||||
|
118
src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
Normal file
118
src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use crate::reference::DEREF_ADDROF;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{get_parent_expr, is_lint_allowed};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `&*(&T)`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Dereferencing and then borrowing a reference value has no effect in most cases.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// false negative on such code:
|
||||
/// ```
|
||||
/// let x = &12;
|
||||
/// let addr_x = &x as *const _ as usize;
|
||||
/// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggerd.
|
||||
/// // But if we fix it, assert will fail.
|
||||
/// assert_ne!(addr_x, addr_y);
|
||||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let s = &String::new();
|
||||
///
|
||||
/// // Bad
|
||||
/// let a: &String = &* s;
|
||||
/// foo(&*s);
|
||||
///
|
||||
/// // Good
|
||||
/// let a: &String = s;
|
||||
/// foo(&**s);
|
||||
///
|
||||
/// fn foo(_: &str){ }
|
||||
/// ```
|
||||
#[clippy::version = "1.59.0"]
|
||||
pub BORROW_DEREF_REF,
|
||||
complexity,
|
||||
"deref on an immutable reference returns the same type as itself"
|
||||
}
|
||||
|
||||
declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]);
|
||||
|
||||
impl LateLintPass<'_> for BorrowDerefRef {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, e: &rustc_hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if !e.span.from_expansion();
|
||||
if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind;
|
||||
if !addrof_target.span.from_expansion();
|
||||
if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind;
|
||||
if !deref_target.span.from_expansion();
|
||||
if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) );
|
||||
let ref_ty = cx.typeck_results().expr_ty(deref_target);
|
||||
if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind();
|
||||
then{
|
||||
|
||||
if let Some(parent_expr) = get_parent_expr(cx, e){
|
||||
if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) &&
|
||||
!is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// modification to `&mut &*x` is different from `&mut x`
|
||||
if matches!(deref_target.kind, ExprKind::Path(..)
|
||||
| ExprKind::Field(..)
|
||||
| ExprKind::Index(..)
|
||||
| ExprKind::Unary(UnOp::Deref, ..))
|
||||
&& matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
BORROW_DEREF_REF,
|
||||
e.span,
|
||||
"deref on an immutable reference",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
e.span,
|
||||
"if you would like to reborrow, try removing `&*`",
|
||||
snippet_opt(cx, deref_target.span).unwrap(),
|
||||
Applicability::MachineApplicable
|
||||
);
|
||||
|
||||
// has deref trait -> give 2 help
|
||||
// doesn't have deref trait -> give 1 help
|
||||
if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){
|
||||
if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
diag.span_suggestion(
|
||||
e.span,
|
||||
"if you would like to deref, try using `&**`",
|
||||
format!(
|
||||
"&**{}",
|
||||
&snippet_opt(cx, deref_target.span).unwrap(),
|
||||
),
|
||||
Applicability::MaybeIncorrect
|
||||
);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -28,7 +28,13 @@ declare_clippy_lint! {
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let vec = vec![1_u8];
|
||||
/// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
|
||||
/// let count = vec.iter().filter(|x| **x == 0u8).count();
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// # let vec = vec![1_u8];
|
||||
/// let count = bytecount::count(&vec, 0u8);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NAIVE_BYTECOUNT,
|
||||
|
@ -1,11 +1,10 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_semver::RustcVersion;
|
||||
|
||||
use super::CAST_ABS_TO_UNSIGNED;
|
||||
@ -18,25 +17,28 @@ pub(super) fn check(
|
||||
cast_to: Ty<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
) {
|
||||
if_chain! {
|
||||
if meets_msrv(msrv, msrvs::UNSIGNED_ABS);
|
||||
if cast_from.is_integral();
|
||||
if cast_to.is_integral();
|
||||
if cast_from.is_signed();
|
||||
if !cast_to.is_signed();
|
||||
if let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind;
|
||||
if let method_name = method_path.ident.name.as_str();
|
||||
if method_name == "abs";
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CAST_ABS_TO_UNSIGNED,
|
||||
expr.span,
|
||||
&format!("casting the result of `{}::{}()` to {}", cast_from, method_name, cast_to),
|
||||
"replace with",
|
||||
format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
if meets_msrv(msrv, msrvs::UNSIGNED_ABS)
|
||||
&& let ty::Int(from) = cast_from.kind()
|
||||
&& let ty::Uint(to) = cast_to.kind()
|
||||
&& let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind
|
||||
&& method_path.ident.name.as_str() == "abs"
|
||||
{
|
||||
let span = if from.bit_width() == to.bit_width() {
|
||||
expr.span
|
||||
} else {
|
||||
// if the result of `.unsigned_abs` would be a different type, keep the cast
|
||||
// e.g. `i64 -> usize`, `i16 -> u8`
|
||||
cast_expr.span
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CAST_ABS_TO_UNSIGNED,
|
||||
span,
|
||||
&format!("casting the result of `{cast_from}::abs()` to {cast_to}"),
|
||||
"replace with",
|
||||
format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -318,7 +318,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function:
|
||||
if let QPath::TypeRelative(ty, path) = &path;
|
||||
if path.ident.name.as_str() == function;
|
||||
if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind;
|
||||
if let [int] = &*tp.segments;
|
||||
if let [int] = tp.segments;
|
||||
then {
|
||||
let name = int.ident.name.as_str();
|
||||
candidates.iter().find(|c| &name == *c).copied()
|
||||
@ -332,7 +332,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function:
|
||||
fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
|
||||
if_chain! {
|
||||
if let QPath::Resolved(_, path) = *path;
|
||||
if let [ty] = &*path.segments;
|
||||
if let [ty] = path.segments;
|
||||
then {
|
||||
let name = ty.ident.name.as_str();
|
||||
INTS.iter().find(|c| &name == *c).copied()
|
||||
|
@ -25,7 +25,7 @@ declare_clippy_lint! {
|
||||
/// complexity.
|
||||
///
|
||||
/// ### Example
|
||||
/// No. You'll see it when you get the warning.
|
||||
/// You'll see it when you get the warning.
|
||||
#[clippy::version = "1.35.0"]
|
||||
pub COGNITIVE_COMPLEXITY,
|
||||
nursery,
|
||||
|
@ -41,7 +41,7 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// Should be written:
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// if x && y {
|
||||
|
@ -34,7 +34,7 @@ declare_clippy_lint! {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use std::cmp::Ordering;
|
||||
|
@ -141,7 +141,7 @@ declare_clippy_lint! {
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// Could be written as:
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// println!("Hello World");
|
||||
/// let foo = if … {
|
||||
|
@ -90,7 +90,7 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
|
||||
while let Some(curr) = cursor.next() {
|
||||
if_chain! {
|
||||
if !prev_is_dollar;
|
||||
if let Some(span) = is_crate_keyword(&curr);
|
||||
if let Some(span) = is_crate_keyword(curr);
|
||||
if let Some(next) = cursor.look_ahead(0);
|
||||
if is_token(next, &TokenKind::ModSep);
|
||||
then {
|
||||
@ -103,7 +103,7 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
|
||||
return span;
|
||||
}
|
||||
}
|
||||
prev_is_dollar = is_token(&curr, &TokenKind::Dollar);
|
||||
prev_is_dollar = is_token(curr, &TokenKind::Dollar);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use clippy_utils::{is_in_cfg_test, is_in_test_function};
|
||||
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_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -30,14 +30,27 @@ declare_clippy_lint! {
|
||||
"`dbg!` macro is intended as a debugging tool"
|
||||
}
|
||||
|
||||
declare_lint_pass!(DbgMacro => [DBG_MACRO]);
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct DbgMacro {
|
||||
allow_dbg_in_tests: bool,
|
||||
}
|
||||
|
||||
impl_lint_pass!(DbgMacro => [DBG_MACRO]);
|
||||
|
||||
impl DbgMacro {
|
||||
pub fn new(allow_dbg_in_tests: bool) -> Self {
|
||||
DbgMacro { allow_dbg_in_tests }
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for DbgMacro {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
|
||||
if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) {
|
||||
// we make an exception for test code
|
||||
if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) {
|
||||
// allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
|
||||
if self.allow_dbg_in_tests
|
||||
&& (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
@ -21,7 +21,7 @@ declare_clippy_lint! {
|
||||
/// bar: bool
|
||||
/// }
|
||||
///
|
||||
/// impl std::default::Default for Foo {
|
||||
/// impl Default for Foo {
|
||||
/// fn default() -> Self {
|
||||
/// Self {
|
||||
/// bar: false
|
||||
|
@ -1,16 +1,17 @@
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::paths;
|
||||
use clippy_utils::ty::{implements_trait, is_copy};
|
||||
use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
|
||||
use clippy_utils::{is_lint_allowed, match_def_path};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
|
||||
use rustc_hir::{
|
||||
BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
|
||||
self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource, Unsafety,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::subst::GenericArg;
|
||||
use rustc_middle::ty::{self, BoundConstness, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
@ -224,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
|
||||
fn check_hash_peq<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
span: Span,
|
||||
trait_ref: &TraitRef<'_>,
|
||||
trait_ref: &hir::TraitRef<'_>,
|
||||
ty: Ty<'tcx>,
|
||||
hash_is_automatically_derived: bool,
|
||||
) {
|
||||
@ -277,7 +278,7 @@ fn check_hash_peq<'tcx>(
|
||||
fn check_ord_partial_ord<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
span: Span,
|
||||
trait_ref: &TraitRef<'_>,
|
||||
trait_ref: &hir::TraitRef<'_>,
|
||||
ty: Ty<'tcx>,
|
||||
ord_is_automatically_derived: bool,
|
||||
) {
|
||||
@ -328,7 +329,7 @@ fn check_ord_partial_ord<'tcx>(
|
||||
}
|
||||
|
||||
/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
|
||||
fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
|
||||
fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
|
||||
let clone_id = match cx.tcx.lang_items().clone_trait() {
|
||||
Some(id) if trait_ref.trait_def_id() == Some(id) => id,
|
||||
_ => return,
|
||||
@ -378,7 +379,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &T
|
||||
fn check_unsafe_derive_deserialize<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
item: &Item<'_>,
|
||||
trait_ref: &TraitRef<'_>,
|
||||
trait_ref: &hir::TraitRef<'_>,
|
||||
ty: Ty<'tcx>,
|
||||
) {
|
||||
fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
|
||||
@ -455,13 +456,41 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
|
||||
}
|
||||
|
||||
/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
|
||||
fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
|
||||
fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
|
||||
if_chain! {
|
||||
if let ty::Adt(adt, substs) = ty.kind();
|
||||
if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq);
|
||||
if let Some(peq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::PartialEq);
|
||||
if let Some(def_id) = trait_ref.trait_def_id();
|
||||
if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id);
|
||||
if !implements_trait(cx, ty, eq_trait_def_id, substs);
|
||||
// New `ParamEnv` replacing `T: PartialEq` with `T: Eq`
|
||||
let param_env = ParamEnv::new(
|
||||
cx.tcx.mk_predicates(cx.param_env.caller_bounds().iter().map(|p| {
|
||||
let kind = p.kind();
|
||||
match kind.skip_binder() {
|
||||
PredicateKind::Trait(p)
|
||||
if p.trait_ref.def_id == peq_trait_def_id
|
||||
&& p.trait_ref.substs.get(0) == p.trait_ref.substs.get(1)
|
||||
&& matches!(p.trait_ref.self_ty().kind(), ty::Param(_))
|
||||
&& p.constness == BoundConstness::NotConst
|
||||
&& p.polarity == ImplPolarity::Positive =>
|
||||
{
|
||||
cx.tcx.mk_predicate(kind.rebind(PredicateKind::Trait(TraitPredicate {
|
||||
trait_ref: TraitRef::new(
|
||||
eq_trait_def_id,
|
||||
cx.tcx.mk_substs([GenericArg::from(p.trait_ref.self_ty())].into_iter()),
|
||||
),
|
||||
constness: BoundConstness::NotConst,
|
||||
polarity: ImplPolarity::Positive,
|
||||
})))
|
||||
},
|
||||
_ => p,
|
||||
}
|
||||
})),
|
||||
cx.param_env.reveal(),
|
||||
cx.param_env.constness(),
|
||||
);
|
||||
if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, substs);
|
||||
then {
|
||||
// If all of our fields implement `Eq`, we can implement `Eq` too
|
||||
for variant in adt.variants() {
|
||||
|
60
src/tools/clippy/clippy_lints/src/doc_link_with_quotes.rs
Normal file
60
src/tools/clippy/clippy_lints/src/doc_link_with_quotes.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::{AttrKind, Attribute};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
|
||||
/// outside of code blocks
|
||||
/// ### Why is this bad?
|
||||
/// It is likely a typo when defining an intra-doc link
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// /// See also: ['foo']
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// /// See also: [`foo`]
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub DOC_LINK_WITH_QUOTES,
|
||||
pedantic,
|
||||
"possible typo for an intra-doc link"
|
||||
}
|
||||
declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]);
|
||||
|
||||
impl EarlyLintPass for DocLinkWithQuotes {
|
||||
fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
if let AttrKind::DocComment(_, symbol) = attr.kind {
|
||||
if contains_quote_link(symbol.as_str()) {
|
||||
span_lint(
|
||||
ctx,
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
attr.span,
|
||||
"possible intra-doc link using quotes instead of backticks",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_quote_link(s: &str) -> bool {
|
||||
let mut in_backticks = false;
|
||||
let mut found_opening = false;
|
||||
|
||||
for c in s.chars().tuple_windows::<(char, char)>() {
|
||||
match c {
|
||||
('`', _) => in_backticks = !in_backticks,
|
||||
('[', '\'') if !in_backticks => found_opening = true,
|
||||
('\'', ']') if !in_backticks && found_opening => return true,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
@ -24,7 +24,7 @@ declare_clippy_lint! {
|
||||
/// if x == y || x < y {}
|
||||
/// ```
|
||||
///
|
||||
/// Could be written as:
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
|
@ -13,23 +13,21 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// fn simple_double_parens() -> i32 {
|
||||
/// ((0))
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// # fn foo(bar: usize) {}
|
||||
/// foo((0));
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn simple_no_parens() -> i32 {
|
||||
/// 0
|
||||
/// }
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// # fn foo(bar: usize) {}
|
||||
/// // Bad
|
||||
/// foo((0));
|
||||
///
|
||||
/// // Good
|
||||
/// foo(0);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
@ -22,15 +22,17 @@ declare_clippy_lint! {
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # use std::time::Duration;
|
||||
/// let dur = Duration::new(5, 0);
|
||||
/// # let duration = Duration::new(5, 0);
|
||||
/// let micros = duration.subsec_nanos() / 1_000;
|
||||
/// let millis = duration.subsec_nanos() / 1_000_000;
|
||||
/// ```
|
||||
///
|
||||
/// // 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();
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::time::Duration;
|
||||
/// # let duration = Duration::new(5, 0);
|
||||
/// let micros = duration.subsec_micros();
|
||||
/// let millis = duration.subsec_millis();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DURATION_SUBSEC,
|
||||
|
@ -26,7 +26,7 @@ declare_clippy_lint! {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn a() {}
|
||||
|
@ -23,12 +23,11 @@ declare_clippy_lint! {
|
||||
///
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// enum Test {}
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// #![feature(never_type)]
|
||||
///
|
||||
|
@ -46,7 +46,7 @@ declare_clippy_lint! {
|
||||
/// map.insert(k, v);
|
||||
/// }
|
||||
/// ```
|
||||
/// can both be rewritten as:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::collections::HashMap;
|
||||
/// # let mut map = HashMap::new();
|
||||
|
@ -32,7 +32,7 @@ declare_clippy_lint! {
|
||||
/// BattenbergCake,
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be written as:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// enum Cake {
|
||||
/// BlackForest,
|
||||
|
@ -30,9 +30,9 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// if x + 1 == x + 1 {}
|
||||
/// ```
|
||||
/// or
|
||||
/// ```rust
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// # let a = 3;
|
||||
/// # let b = 4;
|
||||
/// assert_eq!(a, a);
|
||||
|
@ -26,7 +26,7 @@ declare_clippy_lint! {
|
||||
/// do_thing();
|
||||
/// }
|
||||
/// ```
|
||||
/// Should be written
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// if x == Some(2) {
|
||||
/// do_thing();
|
||||
|
@ -31,12 +31,14 @@ declare_clippy_lint! {
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # fn foo(bar: usize) {}
|
||||
/// // Bad
|
||||
/// let x = Box::new(1);
|
||||
/// foo(*x);
|
||||
/// println!("{}", *x);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # fn foo(bar: usize) {}
|
||||
/// let x = 1;
|
||||
/// foo(x);
|
||||
/// println!("{}", x);
|
||||
|
@ -18,7 +18,6 @@ declare_clippy_lint! {
|
||||
/// readability and API.
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// struct S {
|
||||
/// is_pending: bool,
|
||||
@ -27,7 +26,7 @@ declare_clippy_lint! {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// enum S {
|
||||
/// Pending,
|
||||
|
@ -22,8 +22,16 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # use std::io::Write;
|
||||
/// # let bar = "furchtbar";
|
||||
/// // this would be clearer as `eprintln!("foo: {:?}", bar);`
|
||||
/// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
|
||||
/// writeln!(&mut std::io::stdout(), "foo: {:?}", bar).unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::io::Write;
|
||||
/// # let bar = "furchtbar";
|
||||
/// eprintln!("foo: {:?}", bar);
|
||||
/// println!("foo: {:?}", bar);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EXPLICIT_WRITE,
|
||||
|
@ -20,7 +20,6 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// struct Foo(i32);
|
||||
///
|
||||
/// // Bad
|
||||
/// impl From<String> for Foo {
|
||||
/// fn from(s: String) -> Self {
|
||||
/// Foo(s.parse().unwrap())
|
||||
@ -28,8 +27,8 @@ declare_clippy_lint! {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // Good
|
||||
/// struct Foo(i32);
|
||||
///
|
||||
/// impl TryFrom<String> for Foo {
|
||||
|
@ -19,11 +19,12 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let v: f32 = 0.123_456_789_9;
|
||||
/// println!("{}", v); // 0.123_456_789
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let v: f64 = 0.123_456_789_9;
|
||||
/// println!("{}", v); // 0.123_456_789_9
|
||||
/// ```
|
||||
|
@ -35,8 +35,7 @@ declare_clippy_lint! {
|
||||
/// let _ = a.exp() - 1.0;
|
||||
/// ```
|
||||
///
|
||||
/// is better expressed as
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let a = 3f32;
|
||||
/// let _ = a.cbrt();
|
||||
|
@ -25,12 +25,13 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rust
|
||||
///
|
||||
/// // Bad
|
||||
/// let foo = "foo";
|
||||
/// format!("{}", foo);
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let foo = "foo";
|
||||
/// foo.to_owned();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
@ -36,12 +36,18 @@ declare_clippy_lint! {
|
||||
/// This is either a typo in the binary operator or confusing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// if foo <- 30 { // this should be `foo < -30` but looks like a different operator
|
||||
/// }
|
||||
/// ```rust
|
||||
/// # let foo = true;
|
||||
/// # let bar = false;
|
||||
/// // &&! looks like a different operator
|
||||
/// if foo &&! bar {}
|
||||
/// ```
|
||||
///
|
||||
/// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator
|
||||
/// }
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let foo = true;
|
||||
/// # let bar = false;
|
||||
/// if foo && !bar {}
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub SUSPICIOUS_UNARY_OP_FORMATTING,
|
||||
|
69
src/tools/clippy/clippy_lints/src/get_first.rs
Normal file
69
src/tools/clippy/clippy_lints/src/get_first.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{is_slice_of_primitives, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for using `x.get(0)` instead of
|
||||
/// `x.first()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using `x.first()` is easier to read and has the same
|
||||
/// result.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let first_element = x.get(0);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // Good
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let first_element = x.first();
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub GET_FIRST,
|
||||
style,
|
||||
"Using `x.get(0)` when `x.first()` is simpler"
|
||||
}
|
||||
declare_lint_pass!(GetFirst => [GET_FIRST]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for GetFirst {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &expr.kind;
|
||||
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if match_def_path(cx, expr_def_id, &paths::SLICE_GET);
|
||||
|
||||
if let Some(_) = is_slice_of_primitives(cx, struct_calling_on);
|
||||
if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind;
|
||||
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let slice_name = snippet_with_applicability(
|
||||
cx,
|
||||
struct_calling_on.span, "..",
|
||||
&mut applicability,
|
||||
);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
GET_FIRST,
|
||||
expr.span,
|
||||
&format!("accessing first element with `{0}.get(0)`", slice_name),
|
||||
"try",
|
||||
format!("{}.first()", slice_name),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
//! lint on using `x.get(x.len() - 1)` instead of `x.last()`
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::SpanlessEq;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for using `x.get(x.len() - 1)` instead of
|
||||
/// `x.last()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using `x.last()` is easier to read and has the same
|
||||
/// result.
|
||||
///
|
||||
/// Note that using `x[x.len() - 1]` is semantically different from
|
||||
/// `x.last()`. Indexing into the array will panic on out-of-bounds
|
||||
/// accesses, while `x.get()` and `x.last()` will return `None`.
|
||||
///
|
||||
/// There is another lint (get_unwrap) that covers the case of using
|
||||
/// `x.get(index).unwrap()` instead of `x[index]`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let last_element = x.get(x.len() - 1);
|
||||
///
|
||||
/// // Good
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let last_element = x.last();
|
||||
/// ```
|
||||
#[clippy::version = "1.37.0"]
|
||||
pub GET_LAST_WITH_LEN,
|
||||
complexity,
|
||||
"Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
|
||||
}
|
||||
|
||||
declare_lint_pass!(GetLastWithLen => [GET_LAST_WITH_LEN]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for GetLastWithLen {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
// Is a method call
|
||||
if let ExprKind::MethodCall(path, args, _) = expr.kind;
|
||||
|
||||
// Method name is "get"
|
||||
if path.ident.name == sym!(get);
|
||||
|
||||
// Argument 0 (the struct we're calling the method on) is a vector
|
||||
if let Some(struct_calling_on) = args.get(0);
|
||||
let struct_ty = cx.typeck_results().expr_ty(struct_calling_on);
|
||||
if is_type_diagnostic_item(cx, struct_ty, sym::Vec);
|
||||
|
||||
// Argument to "get" is a subtraction
|
||||
if let Some(get_index_arg) = args.get(1);
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Sub,
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = &get_index_arg.kind;
|
||||
|
||||
// LHS of subtraction is "x.len()"
|
||||
if let ExprKind::MethodCall(arg_lhs_path, lhs_args, _) = &lhs.kind;
|
||||
if arg_lhs_path.ident.name == sym::len;
|
||||
if let Some(arg_lhs_struct) = lhs_args.get(0);
|
||||
|
||||
// The two vectors referenced (x in x.get(...) and in x.len())
|
||||
if SpanlessEq::new(cx).eq_expr(struct_calling_on, arg_lhs_struct);
|
||||
|
||||
// RHS of subtraction is 1
|
||||
if let ExprKind::Lit(rhs_lit) = &rhs.kind;
|
||||
if let LitKind::Int(1, ..) = rhs_lit.node;
|
||||
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let vec_name = snippet_with_applicability(
|
||||
cx,
|
||||
struct_calling_on.span, "vec",
|
||||
&mut applicability,
|
||||
);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
GET_LAST_WITH_LEN,
|
||||
expr.span,
|
||||
&format!("accessing last element with `{0}.get({0}.len() - 1)`", vec_name),
|
||||
"try",
|
||||
format!("{}.last()", vec_name),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +1,14 @@
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind};
|
||||
use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{clip, unsext};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{clip, unsext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for identity operations, e.g., `x + 0`.
|
||||
@ -23,11 +22,6 @@ declare_clippy_lint! {
|
||||
/// # let x = 1;
|
||||
/// x / 1 + 0 * 1 - 0 | 0;
|
||||
/// ```
|
||||
///
|
||||
/// ### Known problems
|
||||
/// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to
|
||||
/// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code.
|
||||
/// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724)
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub IDENTITY_OP,
|
||||
complexity,
|
||||
@ -45,31 +39,22 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp {
|
||||
if !is_allowed(cx, *cmp, left, right) {
|
||||
match cmp.node {
|
||||
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
|
||||
if reducible_to_right(cx, expr, right) {
|
||||
check(cx, left, 0, expr.span, right.span);
|
||||
}
|
||||
check(cx, right, 0, expr.span, left.span);
|
||||
check(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right));
|
||||
check(cx, right, 0, expr.span, left.span, Parens::Unneeded);
|
||||
},
|
||||
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
|
||||
check(cx, right, 0, expr.span, left.span);
|
||||
check(cx, right, 0, expr.span, left.span, Parens::Unneeded);
|
||||
},
|
||||
BinOpKind::Mul => {
|
||||
if reducible_to_right(cx, expr, right) {
|
||||
check(cx, left, 1, expr.span, right.span);
|
||||
}
|
||||
check(cx, right, 1, expr.span, left.span);
|
||||
check(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right));
|
||||
check(cx, right, 1, expr.span, left.span, Parens::Unneeded);
|
||||
},
|
||||
BinOpKind::Div => check(cx, right, 1, expr.span, left.span),
|
||||
BinOpKind::Div => check(cx, right, 1, expr.span, left.span, Parens::Unneeded),
|
||||
BinOpKind::BitAnd => {
|
||||
if reducible_to_right(cx, expr, right) {
|
||||
check(cx, left, -1, expr.span, right.span);
|
||||
}
|
||||
check(cx, right, -1, expr.span, left.span);
|
||||
},
|
||||
BinOpKind::Rem => {
|
||||
// Don't call reducible_to_right because N % N is always reducible to 1
|
||||
check_remainder(cx, left, right, expr.span, left.span);
|
||||
check(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right));
|
||||
check(cx, right, -1, expr.span, left.span, Parens::Unneeded);
|
||||
},
|
||||
BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@ -77,24 +62,50 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if `left op ..right` can be actually reduced to `right`
|
||||
/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
|
||||
/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }`
|
||||
/// See #8724
|
||||
fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind {
|
||||
is_toplevel_binary(cx, binary)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
#[derive(Copy, Clone)]
|
||||
enum Parens {
|
||||
Needed,
|
||||
Unneeded,
|
||||
}
|
||||
|
||||
fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool {
|
||||
if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
/// Checks if `left op right` needs parenthesis when reduced to `right`
|
||||
/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced
|
||||
/// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be
|
||||
/// interpreted as a statement
|
||||
///
|
||||
/// See #8724
|
||||
fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> Parens {
|
||||
match right.kind {
|
||||
ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => {
|
||||
// ensure we're checking against the leftmost expression of `right`
|
||||
//
|
||||
// ~~~ `lhs`
|
||||
// 0 + {4} * 2
|
||||
// ~~~~~~~ `right`
|
||||
return needs_parenthesis(cx, binary, lhs);
|
||||
},
|
||||
ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {},
|
||||
_ => return Parens::Unneeded,
|
||||
}
|
||||
|
||||
let mut prev_id = binary.hir_id;
|
||||
for (_, node) in cx.tcx.hir().parent_iter(binary.hir_id) {
|
||||
if let Node::Expr(expr) = node
|
||||
&& let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) = expr.kind
|
||||
&& lhs.hir_id == prev_id
|
||||
{
|
||||
// keep going until we find a node that encompasses left of `binary`
|
||||
prev_id = expr.hir_id;
|
||||
continue;
|
||||
}
|
||||
|
||||
match node {
|
||||
Node::Block(_) | Node::Stmt(_) => break,
|
||||
_ => return Parens::Unneeded,
|
||||
};
|
||||
}
|
||||
|
||||
Parens::Needed
|
||||
}
|
||||
|
||||
fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
@ -115,11 +126,11 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
|
||||
(Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
|
||||
_ => return,
|
||||
} {
|
||||
span_ineffective_operation(cx, span, arg);
|
||||
span_ineffective_operation(cx, span, arg, Parens::Unneeded);
|
||||
}
|
||||
}
|
||||
|
||||
fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
|
||||
fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) {
|
||||
if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
|
||||
let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
|
||||
ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
|
||||
@ -132,19 +143,27 @@ fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
|
||||
1 => v == 1,
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
span_ineffective_operation(cx, span, arg);
|
||||
span_ineffective_operation(cx, span, arg, parens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) {
|
||||
span_lint(
|
||||
fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span, parens: Parens) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let expr_snippet = snippet_with_applicability(cx, arg, "..", &mut applicability);
|
||||
|
||||
let suggestion = match parens {
|
||||
Parens::Needed => format!("({expr_snippet})"),
|
||||
Parens::Unneeded => expr_snippet.into_owned(),
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
IDENTITY_OP,
|
||||
span,
|
||||
&format!(
|
||||
"the operation is ineffective. Consider reducing it to `{}`",
|
||||
snippet(cx, arg, "..")
|
||||
),
|
||||
"this operation has no effect",
|
||||
"consider reducing it to",
|
||||
suggestion,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
//! lint when there is a large size difference between variants on an enum
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{diagnostics::span_lint_and_then, ty::is_copy};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{Adt, Ty};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
@ -26,6 +27,15 @@ declare_clippy_lint! {
|
||||
/// the overhead is negligible and the boxing is counter-productive. Always
|
||||
/// measure the change this lint suggests.
|
||||
///
|
||||
/// For types that implement `Copy`, the suggestion to `Box` a variant's
|
||||
/// data would require removing the trait impl. The types can of course
|
||||
/// still be `Clone`, but that is worse ergonomically. Depending on the
|
||||
/// use case it may be possible to store the large data in an auxillary
|
||||
/// structure (e.g. Arena or ECS).
|
||||
///
|
||||
/// The lint will ignore generic types if the layout depends on the
|
||||
/// generics, even if the size difference will be large anyway.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
@ -74,7 +84,7 @@ struct VariantInfo {
|
||||
impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
|
||||
if in_external_macro(cx.tcx.sess, item.span) {
|
||||
return;
|
||||
}
|
||||
@ -132,37 +142,43 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
|
||||
let fields = def.variants[variants_size[0].ind].data.fields();
|
||||
variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let sugg: Vec<(Span, String)> = variants_size[0]
|
||||
.fields_size
|
||||
.iter()
|
||||
.rev()
|
||||
.map_while(|val| {
|
||||
if difference > self.maximum_size_difference_allowed {
|
||||
difference = difference.saturating_sub(val.size);
|
||||
Some((
|
||||
fields[val.ind].ty.span,
|
||||
format!(
|
||||
"Box<{}>",
|
||||
snippet_with_applicability(
|
||||
cx,
|
||||
fields[val.ind].ty.span,
|
||||
"..",
|
||||
&mut applicability
|
||||
)
|
||||
.into_owned()
|
||||
),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if is_copy(cx, ty) || maybe_copy(cx, ty) {
|
||||
diag.span_note(
|
||||
item.ident.span,
|
||||
"boxing a variant would require the type no longer be `Copy`",
|
||||
);
|
||||
} else {
|
||||
let sugg: Vec<(Span, String)> = variants_size[0]
|
||||
.fields_size
|
||||
.iter()
|
||||
.rev()
|
||||
.map_while(|val| {
|
||||
if difference > self.maximum_size_difference_allowed {
|
||||
difference = difference.saturating_sub(val.size);
|
||||
Some((
|
||||
fields[val.ind].ty.span,
|
||||
format!(
|
||||
"Box<{}>",
|
||||
snippet_with_applicability(
|
||||
cx,
|
||||
fields[val.ind].ty.span,
|
||||
"..",
|
||||
&mut applicability
|
||||
)
|
||||
.into_owned()
|
||||
),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !sugg.is_empty() {
|
||||
diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect);
|
||||
return;
|
||||
if !sugg.is_empty() {
|
||||
diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
diag.span_help(def.variants[variants_size[0].ind].span, help_text);
|
||||
},
|
||||
);
|
||||
@ -170,3 +186,13 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
if let Adt(_def, substs) = ty.kind()
|
||||
&& substs.types().next().is_some()
|
||||
&& let Some(copy_trait) = cx.tcx.lang_items().copy_trait()
|
||||
{
|
||||
return cx.tcx.non_blanket_impls_for_ty(copy_trait, ty).next().is_some();
|
||||
}
|
||||
false
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
||||
if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind;
|
||||
if !is_local_used(cx, *cond, canonical_id);
|
||||
if let hir::ExprKind::Block(then, _) = then.kind;
|
||||
if let Some(value) = check_assign(cx, canonical_id, &*then);
|
||||
if let Some(value) = check_assign(cx, canonical_id, then);
|
||||
if !is_local_used(cx, value, canonical_id);
|
||||
then {
|
||||
let span = stmt.span.to(if_.span);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
|
||||
LintId::of(approx_const::APPROX_CONSTANT),
|
||||
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
|
||||
LintId::of(assign_ops::ASSIGN_OP_PATTERN),
|
||||
@ -24,6 +25,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||
LintId::of(booleans::LOGIC_BUG),
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
|
||||
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
|
||||
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
|
||||
@ -36,7 +38,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(casts::UNNECESSARY_CAST),
|
||||
LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
|
||||
LintId::of(collapsible_if::COLLAPSIBLE_IF),
|
||||
LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
|
||||
LintId::of(comparison_chain::COMPARISON_CHAIN),
|
||||
LintId::of(copies::IFS_SAME_COND),
|
||||
LintId::of(copies::IF_SAME_THEN_ELSE),
|
||||
@ -91,7 +92,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
|
||||
LintId::of(functions::RESULT_UNIT_ERR),
|
||||
LintId::of(functions::TOO_MANY_ARGUMENTS),
|
||||
LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
|
||||
LintId::of(get_first::GET_FIRST),
|
||||
LintId::of(identity_op::IDENTITY_OP),
|
||||
LintId::of(if_let_mutex::IF_LET_MUTEX),
|
||||
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
|
||||
@ -132,23 +133,25 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(main_recursion::MAIN_RECURSION),
|
||||
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
|
||||
LintId::of(manual_bits::MANUAL_BITS),
|
||||
LintId::of(manual_map::MANUAL_MAP),
|
||||
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(manual_strip::MANUAL_STRIP),
|
||||
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
|
||||
LintId::of(map_clone::MAP_CLONE),
|
||||
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
|
||||
LintId::of(match_result_ok::MATCH_RESULT_OK),
|
||||
LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH),
|
||||
LintId::of(matches::COLLAPSIBLE_MATCH),
|
||||
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
|
||||
LintId::of(matches::MANUAL_MAP),
|
||||
LintId::of(matches::MANUAL_UNWRAP_OR),
|
||||
LintId::of(matches::MATCH_AS_REF),
|
||||
LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
|
||||
LintId::of(matches::MATCH_OVERLAPPING_ARM),
|
||||
LintId::of(matches::MATCH_REF_PATS),
|
||||
LintId::of(matches::MATCH_SINGLE_BINDING),
|
||||
LintId::of(matches::MATCH_STR_CASE_MISMATCH),
|
||||
LintId::of(matches::NEEDLESS_MATCH),
|
||||
LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
|
||||
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
|
||||
LintId::of(matches::SINGLE_MATCH),
|
||||
LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
|
||||
LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
|
||||
@ -166,6 +169,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(methods::FILTER_MAP_IDENTITY),
|
||||
LintId::of(methods::FILTER_NEXT),
|
||||
LintId::of(methods::FLAT_MAP_IDENTITY),
|
||||
LintId::of(methods::GET_LAST_WITH_LEN),
|
||||
LintId::of(methods::INSPECT_FOR_EACH),
|
||||
LintId::of(methods::INTO_ITER_ON_REF),
|
||||
LintId::of(methods::IS_DIGIT_ASCII_RADIX),
|
||||
@ -189,6 +193,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(methods::NEEDLESS_OPTION_TAKE),
|
||||
LintId::of(methods::NEEDLESS_SPLITN),
|
||||
LintId::of(methods::NEW_RET_NO_SELF),
|
||||
LintId::of(methods::NO_EFFECT_REPLACE),
|
||||
LintId::of(methods::OK_EXPECT),
|
||||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||
LintId::of(methods::OPTION_FILTER_MAP),
|
||||
@ -278,7 +283,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(self_assignment::SELF_ASSIGNMENT),
|
||||
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
|
||||
LintId::of(serde_api::SERDE_API_MISUSE),
|
||||
LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE),
|
||||
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
||||
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
|
||||
@ -289,6 +293,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
LintId::of(swap::ALMOST_SWAPPED),
|
||||
LintId::of(swap::MANUAL_SWAP),
|
||||
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
|
||||
LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
|
||||
LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
|
||||
LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
|
||||
@ -302,6 +307,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
|
||||
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||
LintId::of(transmute::USELESS_TRANSMUTE),
|
||||
LintId::of(transmute::WRONG_TRANSMUTE),
|
||||
LintId::of(transmuting_null::TRANSMUTING_NULL),
|
||||
LintId::of(types::BORROWED_BOX),
|
||||
|
@ -5,6 +5,7 @@
|
||||
store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
|
||||
LintId::of(attrs::DEPRECATED_CFG_ATTR),
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
|
||||
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(casts::CHAR_LIT_AS_U8),
|
||||
LintId::of(casts::UNNECESSARY_CAST),
|
||||
@ -15,7 +16,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(explicit_write::EXPLICIT_WRITE),
|
||||
LintId::of(format::USELESS_FORMAT),
|
||||
LintId::of(functions::TOO_MANY_ARGUMENTS),
|
||||
LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
|
||||
LintId::of(identity_op::IDENTITY_OP),
|
||||
LintId::of(int_plus_one::INT_PLUS_ONE),
|
||||
LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
|
||||
@ -25,9 +25,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(loops::SINGLE_ELEMENT_LOOP),
|
||||
LintId::of(loops::WHILE_LET_LOOP),
|
||||
LintId::of(manual_strip::MANUAL_STRIP),
|
||||
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
|
||||
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
|
||||
LintId::of(matches::MANUAL_UNWRAP_OR),
|
||||
LintId::of(matches::MATCH_AS_REF),
|
||||
LintId::of(matches::MATCH_SINGLE_BINDING),
|
||||
LintId::of(matches::NEEDLESS_MATCH),
|
||||
@ -37,6 +37,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(methods::FILTER_MAP_IDENTITY),
|
||||
LintId::of(methods::FILTER_NEXT),
|
||||
LintId::of(methods::FLAT_MAP_IDENTITY),
|
||||
LintId::of(methods::GET_LAST_WITH_LEN),
|
||||
LintId::of(methods::INSPECT_FOR_EACH),
|
||||
LintId::of(methods::ITER_COUNT),
|
||||
LintId::of(methods::MANUAL_FILTER_MAP),
|
||||
@ -90,6 +91,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
|
||||
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
|
||||
LintId::of(transmute::USELESS_TRANSMUTE),
|
||||
LintId::of(types::BORROWED_BOX),
|
||||
LintId::of(types::TYPE_COMPLEXITY),
|
||||
LintId::of(types::VEC_BOX),
|
||||
|
@ -39,7 +39,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
|
||||
LintId::of(loops::ITER_NEXT_LOOP),
|
||||
LintId::of(loops::NEVER_LOOP),
|
||||
LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
|
||||
LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH),
|
||||
LintId::of(matches::MATCH_STR_CASE_MISMATCH),
|
||||
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
|
||||
LintId::of(methods::CLONE_DOUBLE_REF),
|
||||
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
|
||||
|
@ -34,10 +34,10 @@ store.register_lints(&[
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::UNNECESSARY_SYMBOL_STR,
|
||||
absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
|
||||
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
|
||||
approx_const::APPROX_CONSTANT,
|
||||
arithmetic::FLOAT_ARITHMETIC,
|
||||
arithmetic::INTEGER_ARITHMETIC,
|
||||
as_conversions::AS_CONVERSIONS,
|
||||
as_underscore::AS_UNDERSCORE,
|
||||
asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
|
||||
asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
|
||||
assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
|
||||
@ -64,6 +64,7 @@ store.register_lints(&[
|
||||
booleans::LOGIC_BUG,
|
||||
booleans::NONMINIMAL_BOOL,
|
||||
borrow_as_ptr::BORROW_AS_PTR,
|
||||
borrow_deref_ref::BORROW_DEREF_REF,
|
||||
bytecount::NAIVE_BYTECOUNT,
|
||||
bytes_count_to_len::BYTES_COUNT_TO_LEN,
|
||||
cargo::CARGO_COMMON_METADATA,
|
||||
@ -93,7 +94,6 @@ store.register_lints(&[
|
||||
cognitive_complexity::COGNITIVE_COMPLEXITY,
|
||||
collapsible_if::COLLAPSIBLE_ELSE_IF,
|
||||
collapsible_if::COLLAPSIBLE_IF,
|
||||
collapsible_match::COLLAPSIBLE_MATCH,
|
||||
comparison_chain::COMPARISON_CHAIN,
|
||||
copies::BRANCHES_SHARING_CODE,
|
||||
copies::IFS_SAME_COND,
|
||||
@ -124,6 +124,7 @@ store.register_lints(&[
|
||||
doc::MISSING_PANICS_DOC,
|
||||
doc::MISSING_SAFETY_DOC,
|
||||
doc::NEEDLESS_DOCTEST_MAIN,
|
||||
doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
|
||||
double_comparison::DOUBLE_COMPARISONS,
|
||||
double_parens::DOUBLE_PARENS,
|
||||
drop_forget_ref::DROP_COPY,
|
||||
@ -183,7 +184,7 @@ store.register_lints(&[
|
||||
functions::TOO_MANY_ARGUMENTS,
|
||||
functions::TOO_MANY_LINES,
|
||||
future_not_send::FUTURE_NOT_SEND,
|
||||
get_last_with_len::GET_LAST_WITH_LEN,
|
||||
get_first::GET_FIRST,
|
||||
identity_op::IDENTITY_OP,
|
||||
if_let_mutex::IF_LET_MUTEX,
|
||||
if_not_else::IF_NOT_ELSE,
|
||||
@ -250,33 +251,36 @@ store.register_lints(&[
|
||||
manual_assert::MANUAL_ASSERT,
|
||||
manual_async_fn::MANUAL_ASYNC_FN,
|
||||
manual_bits::MANUAL_BITS,
|
||||
manual_map::MANUAL_MAP,
|
||||
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
|
||||
manual_ok_or::MANUAL_OK_OR,
|
||||
manual_strip::MANUAL_STRIP,
|
||||
manual_unwrap_or::MANUAL_UNWRAP_OR,
|
||||
map_clone::MAP_CLONE,
|
||||
map_err_ignore::MAP_ERR_IGNORE,
|
||||
map_unit_fn::OPTION_MAP_UNIT_FN,
|
||||
map_unit_fn::RESULT_MAP_UNIT_FN,
|
||||
match_on_vec_items::MATCH_ON_VEC_ITEMS,
|
||||
match_result_ok::MATCH_RESULT_OK,
|
||||
match_str_case_mismatch::MATCH_STR_CASE_MISMATCH,
|
||||
matches::COLLAPSIBLE_MATCH,
|
||||
matches::INFALLIBLE_DESTRUCTURING_MATCH,
|
||||
matches::MANUAL_MAP,
|
||||
matches::MANUAL_UNWRAP_OR,
|
||||
matches::MATCH_AS_REF,
|
||||
matches::MATCH_BOOL,
|
||||
matches::MATCH_LIKE_MATCHES_MACRO,
|
||||
matches::MATCH_ON_VEC_ITEMS,
|
||||
matches::MATCH_OVERLAPPING_ARM,
|
||||
matches::MATCH_REF_PATS,
|
||||
matches::MATCH_SAME_ARMS,
|
||||
matches::MATCH_SINGLE_BINDING,
|
||||
matches::MATCH_STR_CASE_MISMATCH,
|
||||
matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
|
||||
matches::MATCH_WILD_ERR_ARM,
|
||||
matches::NEEDLESS_MATCH,
|
||||
matches::REDUNDANT_PATTERN_MATCHING,
|
||||
matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
|
||||
matches::SIGNIFICANT_DROP_IN_SCRUTINEE,
|
||||
matches::SINGLE_MATCH,
|
||||
matches::SINGLE_MATCH_ELSE,
|
||||
matches::TRY_ERR,
|
||||
matches::WILDCARD_ENUM_MATCH_ARM,
|
||||
matches::WILDCARD_IN_OR_PATTERNS,
|
||||
mem_forget::MEM_FORGET,
|
||||
@ -302,6 +306,7 @@ store.register_lints(&[
|
||||
methods::FLAT_MAP_IDENTITY,
|
||||
methods::FLAT_MAP_OPTION,
|
||||
methods::FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
methods::GET_LAST_WITH_LEN,
|
||||
methods::GET_UNWRAP,
|
||||
methods::IMPLICIT_CLONE,
|
||||
methods::INEFFICIENT_TO_STRING,
|
||||
@ -330,6 +335,7 @@ store.register_lints(&[
|
||||
methods::NEEDLESS_OPTION_TAKE,
|
||||
methods::NEEDLESS_SPLITN,
|
||||
methods::NEW_RET_NO_SELF,
|
||||
methods::NO_EFFECT_REPLACE,
|
||||
methods::OK_EXPECT,
|
||||
methods::OPTION_AS_REF_DEREF,
|
||||
methods::OPTION_FILTER_MAP,
|
||||
@ -377,6 +383,7 @@ store.register_lints(&[
|
||||
misc_early::UNNEEDED_WILDCARD_PATTERN,
|
||||
misc_early::UNSEPARATED_LITERAL_SUFFIX,
|
||||
misc_early::ZERO_PREFIXED_LITERAL,
|
||||
mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER,
|
||||
missing_const_for_fn::MISSING_CONST_FOR_FN,
|
||||
missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
|
||||
missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
|
||||
@ -418,6 +425,8 @@ store.register_lints(&[
|
||||
non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
|
||||
non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
|
||||
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
|
||||
numeric_arithmetic::FLOAT_ARITHMETIC,
|
||||
numeric_arithmetic::INTEGER_ARITHMETIC,
|
||||
octal_escapes::OCTAL_ESCAPES,
|
||||
only_used_in_recursion::ONLY_USED_IN_RECURSION,
|
||||
open_options::NONSENSICAL_OPEN_OPTIONS,
|
||||
@ -473,7 +482,6 @@ store.register_lints(&[
|
||||
shadow::SHADOW_REUSE,
|
||||
shadow::SHADOW_SAME,
|
||||
shadow::SHADOW_UNRELATED,
|
||||
significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE,
|
||||
single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
|
||||
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
|
||||
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
|
||||
@ -493,6 +501,7 @@ store.register_lints(&[
|
||||
suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
|
||||
swap::ALMOST_SWAPPED,
|
||||
swap::MANUAL_SWAP,
|
||||
swap_ptr_to_ref::SWAP_PTR_TO_REF,
|
||||
tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
|
||||
temporary_assignment::TEMPORARY_ASSIGNMENT,
|
||||
to_digit_is_some::TO_DIGIT_IS_SOME,
|
||||
@ -514,7 +523,6 @@ store.register_lints(&[
|
||||
transmute::USELESS_TRANSMUTE,
|
||||
transmute::WRONG_TRANSMUTE,
|
||||
transmuting_null::TRANSMUTING_NULL,
|
||||
try_err::TRY_ERR,
|
||||
types::BORROWED_BOX,
|
||||
types::BOX_COLLECTION,
|
||||
types::LINKEDLIST,
|
||||
@ -544,6 +552,7 @@ store.register_lints(&[
|
||||
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
|
||||
unused_async::UNUSED_ASYNC,
|
||||
unused_io_amount::UNUSED_IO_AMOUNT,
|
||||
unused_rounding::UNUSED_ROUNDING,
|
||||
unused_self::UNUSED_SELF,
|
||||
unused_unit::UNUSED_UNIT,
|
||||
unwrap::PANICKING_UNWRAP,
|
||||
|
@ -25,11 +25,10 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
||||
LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
|
||||
LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
|
||||
LintId::of(regex::TRIVIAL_REGEX),
|
||||
LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE),
|
||||
LintId::of(strings::STRING_LIT_AS_BYTES),
|
||||
LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
|
||||
LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
|
||||
LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
|
||||
LintId::of(transmute::USELESS_TRANSMUTE),
|
||||
LintId::of(unused_rounding::UNUSED_ROUNDING),
|
||||
LintId::of(use_self::USE_SELF),
|
||||
])
|
||||
|
@ -26,6 +26,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(doc::DOC_MARKDOWN),
|
||||
LintId::of(doc::MISSING_ERRORS_DOC),
|
||||
LintId::of(doc::MISSING_PANICS_DOC),
|
||||
LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES),
|
||||
LintId::of(empty_enum::EMPTY_ENUM),
|
||||
LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
|
||||
LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
|
||||
@ -50,8 +51,8 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(macro_use::MACRO_USE_IMPORTS),
|
||||
LintId::of(manual_assert::MANUAL_ASSERT),
|
||||
LintId::of(manual_ok_or::MANUAL_OK_OR),
|
||||
LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS),
|
||||
LintId::of(matches::MATCH_BOOL),
|
||||
LintId::of(matches::MATCH_ON_VEC_ITEMS),
|
||||
LintId::of(matches::MATCH_SAME_ARMS),
|
||||
LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
|
||||
LintId::of(matches::MATCH_WILD_ERR_ARM),
|
||||
@ -66,6 +67,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(methods::UNNECESSARY_JOIN),
|
||||
LintId::of(misc::FLOAT_CMP),
|
||||
LintId::of(misc::USED_UNDERSCORE_BINDING),
|
||||
LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
|
||||
LintId::of(mut_mut::MUT_MUT),
|
||||
LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
|
||||
LintId::of(needless_continue::NEEDLESS_CONTINUE),
|
||||
|
@ -3,9 +3,8 @@
|
||||
// Manual edits will be overwritten.
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(arithmetic::FLOAT_ARITHMETIC),
|
||||
LintId::of(arithmetic::INTEGER_ARITHMETIC),
|
||||
LintId::of(as_conversions::AS_CONVERSIONS),
|
||||
LintId::of(as_underscore::AS_UNDERSCORE),
|
||||
LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
|
||||
LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
|
||||
LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
|
||||
@ -32,6 +31,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
||||
LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
|
||||
LintId::of(map_err_ignore::MAP_ERR_IGNORE),
|
||||
LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
|
||||
LintId::of(matches::TRY_ERR),
|
||||
LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
|
||||
LintId::of(mem_forget::MEM_FORGET),
|
||||
LintId::of(methods::CLONE_ON_REF_PTR),
|
||||
@ -50,6 +50,8 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
||||
LintId::of(module_style::MOD_MODULE_FILES),
|
||||
LintId::of(module_style::SELF_NAMED_MODULE_FILES),
|
||||
LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
|
||||
LintId::of(numeric_arithmetic::FLOAT_ARITHMETIC),
|
||||
LintId::of(numeric_arithmetic::INTEGER_ARITHMETIC),
|
||||
LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
|
||||
LintId::of(panic_unimplemented::PANIC),
|
||||
LintId::of(panic_unimplemented::TODO),
|
||||
@ -67,7 +69,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
||||
LintId::of(strings::STRING_SLICE),
|
||||
LintId::of(strings::STRING_TO_STRING),
|
||||
LintId::of(strings::STR_TO_STRING),
|
||||
LintId::of(try_err::TRY_ERR),
|
||||
LintId::of(types::RC_BUFFER),
|
||||
LintId::of(types::RC_MUTEX),
|
||||
LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS),
|
||||
|
@ -12,7 +12,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
|
||||
LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
|
||||
LintId::of(collapsible_if::COLLAPSIBLE_IF),
|
||||
LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
|
||||
LintId::of(comparison_chain::COMPARISON_CHAIN),
|
||||
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
|
||||
LintId::of(dereference::NEEDLESS_BORROW),
|
||||
@ -31,6 +30,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(functions::DOUBLE_MUST_USE),
|
||||
LintId::of(functions::MUST_USE_UNIT),
|
||||
LintId::of(functions::RESULT_UNIT_ERR),
|
||||
LintId::of(get_first::GET_FIRST),
|
||||
LintId::of(inherent_to_string::INHERENT_TO_STRING),
|
||||
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
|
||||
LintId::of(len_zero::COMPARISON_TO_EMPTY),
|
||||
@ -45,11 +45,12 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(main_recursion::MAIN_RECURSION),
|
||||
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
|
||||
LintId::of(manual_bits::MANUAL_BITS),
|
||||
LintId::of(manual_map::MANUAL_MAP),
|
||||
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(map_clone::MAP_CLONE),
|
||||
LintId::of(match_result_ok::MATCH_RESULT_OK),
|
||||
LintId::of(matches::COLLAPSIBLE_MATCH),
|
||||
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
|
||||
LintId::of(matches::MANUAL_MAP),
|
||||
LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
|
||||
LintId::of(matches::MATCH_OVERLAPPING_ARM),
|
||||
LintId::of(matches::MATCH_REF_PATS),
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Manual edits will be overwritten.
|
||||
|
||||
store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
|
||||
LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
|
||||
LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
|
||||
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
|
||||
@ -23,11 +24,13 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
|
||||
LintId::of(loops::EMPTY_LOOP),
|
||||
LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
|
||||
LintId::of(loops::MUT_RANGE_BOUND),
|
||||
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
|
||||
LintId::of(methods::NO_EFFECT_REPLACE),
|
||||
LintId::of(methods::SUSPICIOUS_MAP),
|
||||
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
||||
LintId::of(octal_escapes::OCTAL_ESCAPES),
|
||||
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
|
||||
LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE),
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
|
||||
])
|
||||
|
@ -168,9 +168,10 @@ mod renamed_lints;
|
||||
|
||||
// begin lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
mod absurd_extreme_comparisons;
|
||||
mod almost_complete_letter_range;
|
||||
mod approx_const;
|
||||
mod arithmetic;
|
||||
mod as_conversions;
|
||||
mod as_underscore;
|
||||
mod asm_syntax;
|
||||
mod assertions_on_constants;
|
||||
mod assign_ops;
|
||||
@ -183,6 +184,7 @@ mod blocks_in_if_conditions;
|
||||
mod bool_assert_comparison;
|
||||
mod booleans;
|
||||
mod borrow_as_ptr;
|
||||
mod borrow_deref_ref;
|
||||
mod bytecount;
|
||||
mod bytes_count_to_len;
|
||||
mod cargo;
|
||||
@ -191,7 +193,6 @@ mod casts;
|
||||
mod checked_conversions;
|
||||
mod cognitive_complexity;
|
||||
mod collapsible_if;
|
||||
mod collapsible_match;
|
||||
mod comparison_chain;
|
||||
mod copies;
|
||||
mod copy_iterator;
|
||||
@ -208,6 +209,7 @@ mod disallowed_methods;
|
||||
mod disallowed_script_idents;
|
||||
mod disallowed_types;
|
||||
mod doc;
|
||||
mod doc_link_with_quotes;
|
||||
mod double_comparison;
|
||||
mod double_parens;
|
||||
mod drop_forget_ref;
|
||||
@ -242,7 +244,7 @@ mod from_over_into;
|
||||
mod from_str_radix_10;
|
||||
mod functions;
|
||||
mod future_not_send;
|
||||
mod get_last_with_len;
|
||||
mod get_first;
|
||||
mod identity_op;
|
||||
mod if_let_mutex;
|
||||
mod if_not_else;
|
||||
@ -278,17 +280,13 @@ mod main_recursion;
|
||||
mod manual_assert;
|
||||
mod manual_async_fn;
|
||||
mod manual_bits;
|
||||
mod manual_map;
|
||||
mod manual_non_exhaustive;
|
||||
mod manual_ok_or;
|
||||
mod manual_strip;
|
||||
mod manual_unwrap_or;
|
||||
mod map_clone;
|
||||
mod map_err_ignore;
|
||||
mod map_unit_fn;
|
||||
mod match_on_vec_items;
|
||||
mod match_result_ok;
|
||||
mod match_str_case_mismatch;
|
||||
mod matches;
|
||||
mod mem_forget;
|
||||
mod mem_replace;
|
||||
@ -296,6 +294,7 @@ mod methods;
|
||||
mod minmax;
|
||||
mod misc;
|
||||
mod misc_early;
|
||||
mod mismatching_type_param_order;
|
||||
mod missing_const_for_fn;
|
||||
mod missing_doc;
|
||||
mod missing_enforced_import_rename;
|
||||
@ -328,6 +327,7 @@ mod non_expressive_names;
|
||||
mod non_octal_unix_permissions;
|
||||
mod non_send_fields_in_send_ty;
|
||||
mod nonstandard_macro_braces;
|
||||
mod numeric_arithmetic;
|
||||
mod octal_escapes;
|
||||
mod only_used_in_recursion;
|
||||
mod open_options;
|
||||
@ -367,7 +367,6 @@ mod self_named_constructors;
|
||||
mod semicolon_if_nothing_returned;
|
||||
mod serde_api;
|
||||
mod shadow;
|
||||
mod significant_drop_in_scrutinee;
|
||||
mod single_char_lifetime_names;
|
||||
mod single_component_path_imports;
|
||||
mod size_of_in_element_count;
|
||||
@ -378,6 +377,7 @@ mod strlen_on_c_strings;
|
||||
mod suspicious_operation_groupings;
|
||||
mod suspicious_trait_impl;
|
||||
mod swap;
|
||||
mod swap_ptr_to_ref;
|
||||
mod tabs_in_doc_comments;
|
||||
mod temporary_assignment;
|
||||
mod to_digit_is_some;
|
||||
@ -385,7 +385,6 @@ mod trailing_empty_array;
|
||||
mod trait_bounds;
|
||||
mod transmute;
|
||||
mod transmuting_null;
|
||||
mod try_err;
|
||||
mod types;
|
||||
mod undocumented_unsafe_blocks;
|
||||
mod unicode;
|
||||
@ -402,6 +401,7 @@ mod unnested_or_patterns;
|
||||
mod unsafe_removed_from_name;
|
||||
mod unused_async;
|
||||
mod unused_io_amount;
|
||||
mod unused_rounding;
|
||||
mod unused_self;
|
||||
mod unused_unit;
|
||||
mod unwrap;
|
||||
@ -562,7 +562,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(len_zero::LenZero));
|
||||
store.register_late_pass(|| Box::new(attrs::Attributes));
|
||||
store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
|
||||
store.register_late_pass(|| Box::new(collapsible_match::CollapsibleMatch));
|
||||
store.register_late_pass(|| Box::new(unicode::Unicode));
|
||||
store.register_late_pass(|| Box::new(uninit_vec::UninitVec));
|
||||
store.register_late_pass(|| Box::new(unit_hash::UnitHash));
|
||||
@ -636,6 +635,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
|
||||
store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
|
||||
store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
|
||||
store.register_late_pass(|| Box::new(borrow_deref_ref::BorrowDerefRef));
|
||||
store.register_late_pass(|| Box::new(no_effect::NoEffect));
|
||||
store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
|
||||
store.register_late_pass(|| Box::new(transmute::Transmute));
|
||||
@ -652,7 +652,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(strings::StringLitAsBytes));
|
||||
store.register_late_pass(|| Box::new(derive::Derive));
|
||||
store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls));
|
||||
store.register_late_pass(|| Box::new(get_last_with_len::GetLastWithLen));
|
||||
store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef));
|
||||
store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
|
||||
store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons));
|
||||
@ -678,7 +677,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
|
||||
store.register_late_pass(|| Box::new(neg_multiply::NegMultiply));
|
||||
store.register_late_pass(|| Box::new(mem_forget::MemForget));
|
||||
store.register_late_pass(|| Box::new(arithmetic::Arithmetic::default()));
|
||||
store.register_late_pass(|| Box::new(numeric_arithmetic::NumericArithmetic::default()));
|
||||
store.register_late_pass(|| Box::new(assign_ops::AssignOps));
|
||||
store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq));
|
||||
store.register_late_pass(|| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
|
||||
@ -700,7 +699,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
);
|
||||
store.register_late_pass(move || Box::new(pass_by_ref_or_value));
|
||||
store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
|
||||
store.register_late_pass(|| Box::new(try_err::TryErr));
|
||||
store.register_late_pass(|| Box::new(bytecount::ByteCount));
|
||||
store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
|
||||
store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
|
||||
@ -812,7 +810,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(if_not_else::IfNotElse));
|
||||
store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
|
||||
store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
|
||||
store.register_late_pass(|| Box::new(match_on_vec_items::MatchOnVecItems));
|
||||
store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
|
||||
store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
|
||||
store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
|
||||
@ -830,7 +827,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
|
||||
store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
|
||||
store.register_late_pass(|| Box::new(self_assignment::SelfAssignment));
|
||||
store.register_late_pass(|| Box::new(manual_unwrap_or::ManualUnwrapOr));
|
||||
store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
|
||||
store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs));
|
||||
store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
|
||||
@ -849,7 +845,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
});
|
||||
store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
|
||||
store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
|
||||
store.register_late_pass(|| Box::new(manual_map::ManualMap));
|
||||
store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
|
||||
store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison));
|
||||
store.register_early_pass(move || Box::new(module_style::ModStyle));
|
||||
@ -875,7 +870,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
))
|
||||
});
|
||||
store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
|
||||
store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch));
|
||||
store.register_late_pass(move || Box::new(format_args::FormatArgs));
|
||||
store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray));
|
||||
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
|
||||
@ -886,9 +880,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
|
||||
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
|
||||
store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
|
||||
store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
|
||||
store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
|
||||
store.register_late_pass(|| Box::new(significant_drop_in_scrutinee::SignificantDropInScrutinee));
|
||||
store.register_late_pass(|| Box::new(dbg_macro::DbgMacro));
|
||||
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
|
||||
store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
|
||||
let cargo_ignore_publish = conf.cargo_ignore_publish;
|
||||
store.register_late_pass(move || {
|
||||
Box::new(cargo::Cargo {
|
||||
@ -906,6 +901,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
|
||||
store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
|
||||
store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
|
||||
store.register_late_pass(|| Box::new(get_first::GetFirst));
|
||||
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
|
||||
store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
|
||||
store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef));
|
||||
store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
|
||||
store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
@ -180,29 +180,24 @@ declare_clippy_lint! {
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let opt = Some(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// # let res: Result<i32, std::io::Error> = Ok(1);
|
||||
/// for x in opt {
|
||||
/// // ..
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// if let Some(x) = opt {
|
||||
/// for x in &res {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let opt = Some(1);
|
||||
/// # let res: Result<i32, std::io::Error> = Ok(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// for x in &res {
|
||||
/// if let Some(x) = opt {
|
||||
/// // ..
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// if let Ok(x) = res {
|
||||
/// // ..
|
||||
/// }
|
||||
|
@ -3,9 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::has_iter_method;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{
|
||||
contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq,
|
||||
};
|
||||
use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
@ -29,7 +27,12 @@ pub(super) fn check<'tcx>(
|
||||
body: &'tcx Expr<'_>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
) {
|
||||
if let Some(higher::Range { start: Some(start), ref end, limits }) = higher::Range::hir(arg) {
|
||||
if let Some(higher::Range {
|
||||
start: Some(start),
|
||||
ref end,
|
||||
limits,
|
||||
}) = higher::Range::hir(arg)
|
||||
{
|
||||
// the var must be a single name
|
||||
if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
|
||||
let mut visitor = VarVisitor {
|
||||
@ -104,22 +107,19 @@ pub(super) fn check<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty)
|
||||
{
|
||||
if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
|
||||
String::new()
|
||||
} else if visitor.indexed_mut.contains(&indexed)
|
||||
&& contains_name(indexed, take_expr)
|
||||
{
|
||||
} else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) {
|
||||
return;
|
||||
} else {
|
||||
match limits {
|
||||
ast::RangeLimits::Closed => {
|
||||
let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>");
|
||||
format!(".take({})", take_expr + sugg::ONE)
|
||||
}
|
||||
},
|
||||
ast::RangeLimits::HalfOpen => {
|
||||
format!(".take({})", snippet(cx, take_expr.span, ".."))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -145,10 +145,7 @@ pub(super) fn check<'tcx>(
|
||||
cx,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
arg.span,
|
||||
&format!(
|
||||
"the loop variable `{}` is used to index `{}`",
|
||||
ident.name, indexed
|
||||
),
|
||||
&format!("the loop variable `{}` is used to index `{}`", ident.name, indexed),
|
||||
|diag| {
|
||||
multispan_sugg(
|
||||
diag,
|
||||
@ -157,10 +154,7 @@ pub(super) fn check<'tcx>(
|
||||
(pat.span, format!("({}, <item>)", ident.name)),
|
||||
(
|
||||
arg.span,
|
||||
format!(
|
||||
"{}.{}().enumerate(){}{}",
|
||||
indexed, method, method_1, method_2
|
||||
),
|
||||
format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -177,10 +171,7 @@ pub(super) fn check<'tcx>(
|
||||
cx,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
arg.span,
|
||||
&format!(
|
||||
"the loop variable `{}` is only used to index `{}`",
|
||||
ident.name, indexed
|
||||
),
|
||||
&format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed),
|
||||
|diag| {
|
||||
multispan_sugg(
|
||||
diag,
|
||||
@ -257,12 +248,7 @@ struct VarVisitor<'a, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
|
||||
fn check(
|
||||
&mut self,
|
||||
idx: &'tcx Expr<'_>,
|
||||
seqexpr: &'tcx Expr<'_>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
) -> bool {
|
||||
fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
// the indexed container is referenced by a name
|
||||
if let ExprKind::Path(ref seqpath) = seqexpr.kind;
|
||||
@ -351,13 +337,13 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
|
||||
self.visit_expr(lhs);
|
||||
self.prefer_mutable = false;
|
||||
self.visit_expr(rhs);
|
||||
}
|
||||
},
|
||||
ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => {
|
||||
if mutbl == Mutability::Mut {
|
||||
self.prefer_mutable = true;
|
||||
}
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
},
|
||||
ExprKind::Call(f, args) => {
|
||||
self.visit_expr(f);
|
||||
for expr in args {
|
||||
@ -370,11 +356,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
|
||||
}
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(_, args, _) => {
|
||||
let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
|
||||
for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args)
|
||||
{
|
||||
for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) {
|
||||
self.prefer_mutable = false;
|
||||
if let ty::Ref(_, _, mutbl) = *ty.kind() {
|
||||
if mutbl == Mutability::Mut {
|
||||
@ -383,11 +368,11 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
|
||||
}
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::Closure(_, _, body_id, ..) => {
|
||||
let body = self.cx.tcx.hir().body(body_id);
|
||||
self.visit_expr(&body.value);
|
||||
}
|
||||
},
|
||||
_ => walk_expr(self, expr),
|
||||
}
|
||||
self.prefer_mutable = old;
|
||||
|
@ -146,7 +146,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
||||
if arms.is_empty() {
|
||||
e
|
||||
} else {
|
||||
let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id);
|
||||
let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id);
|
||||
combine_seq(e, arms)
|
||||
}
|
||||
},
|
||||
|
@ -1,316 +0,0 @@
|
||||
use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher::IfLetOrMatch;
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
|
||||
use clippy_utils::{
|
||||
can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
|
||||
peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
|
||||
};
|
||||
use rustc_ast::util::parser::PREC_POSTFIX;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{
|
||||
def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path,
|
||||
QPath, UnsafeSource,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{sym, SyntaxContext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usages of `match` which could be implemented using `map`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using the `map` method is clearer and more concise.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// match Some(0) {
|
||||
/// Some(x) => Some(x + 1),
|
||||
/// None => None,
|
||||
/// };
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// Some(0).map(|x| x + 1);
|
||||
/// ```
|
||||
#[clippy::version = "1.52.0"]
|
||||
pub MANUAL_MAP,
|
||||
style,
|
||||
"reimplementation of `map`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualMap => [MANUAL_MAP]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualMap {
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) {
|
||||
Some(IfLetOrMatch::IfLet(scrutinee, pat, body, Some(r#else))) => (scrutinee, pat, body, None, r#else),
|
||||
Some(IfLetOrMatch::Match(
|
||||
scrutinee,
|
||||
[arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }],
|
||||
_,
|
||||
)) => (scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body),
|
||||
_ => return,
|
||||
};
|
||||
if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let (scrutinee_ty, ty_ref_count, ty_mutability) =
|
||||
peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
|
||||
if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option)
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let expr_ctxt = expr.span.ctxt();
|
||||
let (some_expr, some_pat, pat_ref_count, is_wild_none) = match (
|
||||
try_parse_pattern(cx, then_pat, expr_ctxt),
|
||||
else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)),
|
||||
) {
|
||||
(Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
|
||||
(else_body, pattern, ref_count, true)
|
||||
},
|
||||
(Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
|
||||
(else_body, pattern, ref_count, false)
|
||||
},
|
||||
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => {
|
||||
(then_body, pattern, ref_count, true)
|
||||
},
|
||||
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => {
|
||||
(then_body, pattern, ref_count, false)
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Top level or patterns aren't allowed in closures.
|
||||
if matches!(some_pat.kind, PatKind::Or(_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) {
|
||||
Some(expr) => expr,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// These two lints will go back and forth with each other.
|
||||
if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit
|
||||
&& !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// `map` won't perform any adjustments.
|
||||
if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine which binding mode to use.
|
||||
let explicit_ref = some_pat.contains_explicit_ref_binding();
|
||||
let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
|
||||
|
||||
let as_ref_str = match binding_ref {
|
||||
Some(Mutability::Mut) => ".as_mut()",
|
||||
Some(Mutability::Not) => ".as_ref()",
|
||||
None => "",
|
||||
};
|
||||
|
||||
match can_move_expr_to_closure(cx, some_expr.expr) {
|
||||
Some(captures) => {
|
||||
// Check if captures the closure will need conflict with borrows made in the scrutinee.
|
||||
// TODO: check all the references made in the scrutinee expression. This will require interacting
|
||||
// with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
|
||||
if let Some(binding_ref_mutability) = binding_ref {
|
||||
let e = peel_hir_expr_while(scrutinee, |e| match e.kind {
|
||||
ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
|
||||
_ => None,
|
||||
});
|
||||
if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind {
|
||||
match captures.get(l) {
|
||||
Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return,
|
||||
Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => {
|
||||
return;
|
||||
},
|
||||
Some(CaptureKind::Ref(Mutability::Not)) | None => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
None => return,
|
||||
};
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
|
||||
// Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
|
||||
// it's being passed by value.
|
||||
let scrutinee = peel_hir_expr_refs(scrutinee).0;
|
||||
let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
|
||||
let scrutinee_str =
|
||||
if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
|
||||
format!("({})", scrutinee_str)
|
||||
} else {
|
||||
scrutinee_str.into()
|
||||
};
|
||||
|
||||
let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
|
||||
if_chain! {
|
||||
if !some_expr.needs_unsafe_block;
|
||||
if let Some(func) = can_pass_as_func(cx, id, some_expr.expr);
|
||||
if func.span.ctxt() == some_expr.expr.span.ctxt();
|
||||
then {
|
||||
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
|
||||
} else {
|
||||
if path_to_local_id(some_expr.expr, id)
|
||||
&& !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
|
||||
&& binding_ref.is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// `ref` and `ref mut` annotations were handled earlier.
|
||||
let annotation = if matches!(annotation, BindingAnnotation::Mutable) {
|
||||
"mut "
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
|
||||
if some_expr.needs_unsafe_block {
|
||||
format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip)
|
||||
} else {
|
||||
format!("|{}{}| {}", annotation, some_binding, expr_snip)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !is_wild_none && explicit_ref.is_none() {
|
||||
// TODO: handle explicit reference annotations.
|
||||
let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0;
|
||||
let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
|
||||
if some_expr.needs_unsafe_block {
|
||||
format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip)
|
||||
} else {
|
||||
format!("|{}| {}", pat_snip, expr_snip)
|
||||
}
|
||||
} else {
|
||||
// Refutable bindings and mixed reference annotations can't be handled by `map`.
|
||||
return;
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_MAP,
|
||||
expr.span,
|
||||
"manual implementation of `Option::map`",
|
||||
"try this",
|
||||
if else_pat.is_none() && is_else_clause(cx.tcx, expr) {
|
||||
format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str)
|
||||
} else {
|
||||
format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str)
|
||||
},
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks whether the expression could be passed as a function, or whether a closure is needed.
|
||||
// Returns the function to be passed to `map` if it exists.
|
||||
fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
match expr.kind {
|
||||
ExprKind::Call(func, [arg])
|
||||
if path_to_local_id(arg, binding)
|
||||
&& cx.typeck_results().expr_adjustments(arg).is_empty()
|
||||
&& !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) =>
|
||||
{
|
||||
Some(func)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
enum OptionPat<'a> {
|
||||
Wild,
|
||||
None,
|
||||
Some {
|
||||
// The pattern contained in the `Some` tuple.
|
||||
pattern: &'a Pat<'a>,
|
||||
// The number of references before the `Some` tuple.
|
||||
// e.g. `&&Some(_)` has a ref count of 2.
|
||||
ref_count: usize,
|
||||
},
|
||||
}
|
||||
|
||||
struct SomeExpr<'tcx> {
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
needs_unsafe_block: bool,
|
||||
}
|
||||
|
||||
// Try to parse into a recognized `Option` pattern.
|
||||
// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
|
||||
fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
|
||||
fn f<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
pat: &'tcx Pat<'_>,
|
||||
ref_count: usize,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<OptionPat<'tcx>> {
|
||||
match pat.kind {
|
||||
PatKind::Wild => Some(OptionPat::Wild),
|
||||
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
|
||||
PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
|
||||
PatKind::TupleStruct(ref qpath, [pattern], _)
|
||||
if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
|
||||
{
|
||||
Some(OptionPat::Some { pattern, ref_count })
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
f(cx, pat, 0, ctxt)
|
||||
}
|
||||
|
||||
// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
|
||||
fn get_some_expr<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
needs_unsafe_block: bool,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<SomeExpr<'tcx>> {
|
||||
// TODO: Allow more complex expressions.
|
||||
match expr.kind {
|
||||
ExprKind::Call(
|
||||
Expr {
|
||||
kind: ExprKind::Path(ref qpath),
|
||||
..
|
||||
},
|
||||
[arg],
|
||||
) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr {
|
||||
expr: arg,
|
||||
needs_unsafe_block,
|
||||
}),
|
||||
ExprKind::Block(
|
||||
Block {
|
||||
stmts: [],
|
||||
expr: Some(expr),
|
||||
rules,
|
||||
..
|
||||
},
|
||||
_,
|
||||
) => get_some_expr(
|
||||
cx,
|
||||
expr,
|
||||
needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
|
||||
ctxt,
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Checks for the `None` value.
|
||||
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
|
||||
}
|
@ -113,7 +113,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
|
||||
let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
|
||||
VisibilityKind::Public => None,
|
||||
VisibilityKind::Inherited => Some(Ok(f)),
|
||||
_ => Some(Err(())),
|
||||
VisibilityKind::Restricted { .. } => Some(Err(())),
|
||||
});
|
||||
if let Some(Ok(field)) = iter.next()
|
||||
&& iter.next().is_none()
|
||||
|
@ -1,123 +0,0 @@
|
||||
use clippy_utils::consts::constant_simple;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::usage::contains_return_break_continue_macro;
|
||||
use clippy_utils::{in_constant, is_lang_ctor, path_to_local_id, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LintContext;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Concise code helps focusing on behavior instead of boilerplate.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let foo: Option<i32> = None;
|
||||
/// match foo {
|
||||
/// Some(v) => v,
|
||||
/// None => 1,
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let foo: Option<i32> = None;
|
||||
/// foo.unwrap_or(1);
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub MANUAL_UNWRAP_OR,
|
||||
complexity,
|
||||
"finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOr {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
lint_manual_unwrap_or(cx, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
|
||||
if_chain! {
|
||||
if arms.len() == 2;
|
||||
if arms.iter().all(|arm| arm.guard.is_none());
|
||||
if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| {
|
||||
match arm.pat.kind {
|
||||
PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
|
||||
PatKind::TupleStruct(ref qpath, [pat], _) =>
|
||||
matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr),
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
let unwrap_arm = &arms[1 - idx];
|
||||
if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind;
|
||||
if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
|
||||
if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
|
||||
if path_to_local_id(unwrap_arm.body, binding_hir_id);
|
||||
if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty();
|
||||
if !contains_return_break_continue_macro(or_arm.body);
|
||||
then {
|
||||
Some(or_arm)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind;
|
||||
let ty = cx.typeck_results().expr_ty(scrutinee);
|
||||
if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) {
|
||||
Some("Option")
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::Result) {
|
||||
Some("Result")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(or_arm) = applicable_or_arm(cx, match_arms);
|
||||
if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
|
||||
if let Some(indent) = indent_of(cx, expr.span);
|
||||
if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
|
||||
then {
|
||||
let reindented_or_body =
|
||||
reindent_multiline(or_body_snippet.into(), true, Some(indent));
|
||||
|
||||
let suggestion = if scrutinee.span.from_expansion() {
|
||||
// we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)`
|
||||
sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..")
|
||||
}
|
||||
else {
|
||||
sugg::Sugg::hir(cx, scrutinee, "..").maybe_par()
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_UNWRAP_OR, expr.span,
|
||||
&format!("this pattern reimplements `{}::unwrap_or`", ty_name),
|
||||
"replace with",
|
||||
format!(
|
||||
"{}.unwrap_or({})",
|
||||
suggestion,
|
||||
reindented_or_body,
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `match vec[idx]` or `match vec[n..m]`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This can panic at runtime.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust, no_run
|
||||
/// let arr = vec![0, 1, 2, 3];
|
||||
/// let idx = 1;
|
||||
///
|
||||
/// // Bad
|
||||
/// match arr[idx] {
|
||||
/// 0 => println!("{}", 0),
|
||||
/// 1 => println!("{}", 3),
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust, no_run
|
||||
/// let arr = vec![0, 1, 2, 3];
|
||||
/// let idx = 1;
|
||||
///
|
||||
/// // Good
|
||||
/// match arr.get(idx) {
|
||||
/// Some(0) => println!("{}", 0),
|
||||
/// Some(1) => println!("{}", 3),
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.45.0"]
|
||||
pub MATCH_ON_VEC_ITEMS,
|
||||
pedantic,
|
||||
"matching on vector elements can panic"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MatchOnVecItems => [MATCH_ON_VEC_ITEMS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MatchOnVecItems {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.sess(), expr.span);
|
||||
if let ExprKind::Match(match_expr, _, MatchSource::Normal) = expr.kind;
|
||||
if let Some(idx_expr) = is_vec_indexing(cx, match_expr);
|
||||
if let ExprKind::Index(vec, idx) = idx_expr.kind;
|
||||
|
||||
then {
|
||||
// FIXME: could be improved to suggest surrounding every pattern with Some(_),
|
||||
// but only when `or_patterns` are stabilized.
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MATCH_ON_VEC_ITEMS,
|
||||
match_expr.span,
|
||||
"indexing into a vector may panic",
|
||||
"try this",
|
||||
format!(
|
||||
"{}.get({})",
|
||||
snippet(cx, vec.span, ".."),
|
||||
snippet(cx, idx.span, "..")
|
||||
),
|
||||
Applicability::MaybeIncorrect
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if_chain! {
|
||||
if let ExprKind::Index(array, index) = expr.kind;
|
||||
if is_vector(cx, array);
|
||||
if !is_full_range(cx, index);
|
||||
|
||||
then {
|
||||
return Some(expr);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
let ty = ty.peel_refs();
|
||||
is_type_diagnostic_item(cx, ty, sym::Vec)
|
||||
}
|
||||
|
||||
fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
let ty = ty.peel_refs();
|
||||
is_type_lang_item(cx, ty, LangItem::RangeFull)
|
||||
}
|
@ -6,68 +6,28 @@ use if_chain::if_chain;
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together
|
||||
/// without adding any branches.
|
||||
///
|
||||
/// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
|
||||
/// cases where merging would most likely make the code more readable.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is unnecessarily verbose and complex.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn func(opt: Option<Result<u64, String>>) {
|
||||
/// let n = match opt {
|
||||
/// Some(n) => match n {
|
||||
/// Ok(n) => n,
|
||||
/// _ => return,
|
||||
/// }
|
||||
/// None => return,
|
||||
/// };
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn func(opt: Option<Result<u64, String>>) {
|
||||
/// let n = match opt {
|
||||
/// Some(Ok(n)) => n,
|
||||
/// _ => return,
|
||||
/// };
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.50.0"]
|
||||
pub COLLAPSIBLE_MATCH,
|
||||
style,
|
||||
"Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
|
||||
}
|
||||
use super::COLLAPSIBLE_MATCH;
|
||||
|
||||
declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
match IfLetOrMatch::parse(cx, expr) {
|
||||
Some(IfLetOrMatch::Match(_, arms, _)) => {
|
||||
if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
|
||||
for arm in arms {
|
||||
check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body));
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(IfLetOrMatch::IfLet(_, pat, body, els)) => {
|
||||
check_arm(cx, false, pat, body, None, els);
|
||||
},
|
||||
None => {},
|
||||
pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
||||
if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
|
||||
for arm in arms {
|
||||
check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_if_let<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
pat: &'tcx Pat<'_>,
|
||||
body: &'tcx Expr<'_>,
|
||||
else_expr: Option<&'tcx Expr<'_>>,
|
||||
) {
|
||||
check_arm(cx, false, pat, body, None, else_expr);
|
||||
}
|
||||
|
||||
fn check_arm<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
outer_is_match: bool,
|
306
src/tools/clippy/clippy_lints/src/matches/manual_map.rs
Normal file
306
src/tools/clippy/clippy_lints/src/matches/manual_map.rs
Normal file
@ -0,0 +1,306 @@
|
||||
use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
|
||||
use clippy_utils::{
|
||||
can_move_expr_to_closure, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, peel_blocks,
|
||||
peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
|
||||
};
|
||||
use rustc_ast::util::parser::PREC_POSTFIX;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{
|
||||
def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path,
|
||||
QPath, UnsafeSource,
|
||||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{sym, SyntaxContext};
|
||||
|
||||
use super::MANUAL_MAP;
|
||||
|
||||
pub(super) fn check_match<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
scrutinee: &'tcx Expr<'_>,
|
||||
arms: &'tcx [Arm<'_>],
|
||||
) {
|
||||
if let [arm1, arm2] = arms
|
||||
&& arm1.guard.is_none()
|
||||
&& arm2.guard.is_none()
|
||||
{
|
||||
check(cx, expr, scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_if_let<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
let_pat: &'tcx Pat<'_>,
|
||||
let_expr: &'tcx Expr<'_>,
|
||||
then_expr: &'tcx Expr<'_>,
|
||||
else_expr: &'tcx Expr<'_>,
|
||||
) {
|
||||
check(cx, expr, let_expr, let_pat, then_expr, None, else_expr);
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
scrutinee: &'tcx Expr<'_>,
|
||||
then_pat: &'tcx Pat<'_>,
|
||||
then_body: &'tcx Expr<'_>,
|
||||
else_pat: Option<&'tcx Pat<'_>>,
|
||||
else_body: &'tcx Expr<'_>,
|
||||
) {
|
||||
let (scrutinee_ty, ty_ref_count, ty_mutability) =
|
||||
peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
|
||||
if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option)
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let expr_ctxt = expr.span.ctxt();
|
||||
let (some_expr, some_pat, pat_ref_count, is_wild_none) = match (
|
||||
try_parse_pattern(cx, then_pat, expr_ctxt),
|
||||
else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)),
|
||||
) {
|
||||
(Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
|
||||
(else_body, pattern, ref_count, true)
|
||||
},
|
||||
(Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
|
||||
(else_body, pattern, ref_count, false)
|
||||
},
|
||||
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => {
|
||||
(then_body, pattern, ref_count, true)
|
||||
},
|
||||
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => {
|
||||
(then_body, pattern, ref_count, false)
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Top level or patterns aren't allowed in closures.
|
||||
if matches!(some_pat.kind, PatKind::Or(_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) {
|
||||
Some(expr) => expr,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// These two lints will go back and forth with each other.
|
||||
if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit
|
||||
&& !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// `map` won't perform any adjustments.
|
||||
if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine which binding mode to use.
|
||||
let explicit_ref = some_pat.contains_explicit_ref_binding();
|
||||
let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
|
||||
|
||||
let as_ref_str = match binding_ref {
|
||||
Some(Mutability::Mut) => ".as_mut()",
|
||||
Some(Mutability::Not) => ".as_ref()",
|
||||
None => "",
|
||||
};
|
||||
|
||||
match can_move_expr_to_closure(cx, some_expr.expr) {
|
||||
Some(captures) => {
|
||||
// Check if captures the closure will need conflict with borrows made in the scrutinee.
|
||||
// TODO: check all the references made in the scrutinee expression. This will require interacting
|
||||
// with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
|
||||
if let Some(binding_ref_mutability) = binding_ref {
|
||||
let e = peel_hir_expr_while(scrutinee, |e| match e.kind {
|
||||
ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
|
||||
_ => None,
|
||||
});
|
||||
if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind {
|
||||
match captures.get(l) {
|
||||
Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return,
|
||||
Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => {
|
||||
return;
|
||||
},
|
||||
Some(CaptureKind::Ref(Mutability::Not)) | None => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
None => return,
|
||||
};
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
|
||||
// Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
|
||||
// it's being passed by value.
|
||||
let scrutinee = peel_hir_expr_refs(scrutinee).0;
|
||||
let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
|
||||
let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
|
||||
format!("({})", scrutinee_str)
|
||||
} else {
|
||||
scrutinee_str.into()
|
||||
};
|
||||
|
||||
let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
|
||||
if_chain! {
|
||||
if !some_expr.needs_unsafe_block;
|
||||
if let Some(func) = can_pass_as_func(cx, id, some_expr.expr);
|
||||
if func.span.ctxt() == some_expr.expr.span.ctxt();
|
||||
then {
|
||||
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
|
||||
} else {
|
||||
if path_to_local_id(some_expr.expr, id)
|
||||
&& !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
|
||||
&& binding_ref.is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// `ref` and `ref mut` annotations were handled earlier.
|
||||
let annotation = if matches!(annotation, BindingAnnotation::Mutable) {
|
||||
"mut "
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
|
||||
if some_expr.needs_unsafe_block {
|
||||
format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip)
|
||||
} else {
|
||||
format!("|{}{}| {}", annotation, some_binding, expr_snip)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !is_wild_none && explicit_ref.is_none() {
|
||||
// TODO: handle explicit reference annotations.
|
||||
let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0;
|
||||
let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
|
||||
if some_expr.needs_unsafe_block {
|
||||
format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip)
|
||||
} else {
|
||||
format!("|{}| {}", pat_snip, expr_snip)
|
||||
}
|
||||
} else {
|
||||
// Refutable bindings and mixed reference annotations can't be handled by `map`.
|
||||
return;
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_MAP,
|
||||
expr.span,
|
||||
"manual implementation of `Option::map`",
|
||||
"try this",
|
||||
if else_pat.is_none() && is_else_clause(cx.tcx, expr) {
|
||||
format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str)
|
||||
} else {
|
||||
format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str)
|
||||
},
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
||||
// Checks whether the expression could be passed as a function, or whether a closure is needed.
|
||||
// Returns the function to be passed to `map` if it exists.
|
||||
fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
match expr.kind {
|
||||
ExprKind::Call(func, [arg])
|
||||
if path_to_local_id(arg, binding)
|
||||
&& cx.typeck_results().expr_adjustments(arg).is_empty()
|
||||
&& !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) =>
|
||||
{
|
||||
Some(func)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
enum OptionPat<'a> {
|
||||
Wild,
|
||||
None,
|
||||
Some {
|
||||
// The pattern contained in the `Some` tuple.
|
||||
pattern: &'a Pat<'a>,
|
||||
// The number of references before the `Some` tuple.
|
||||
// e.g. `&&Some(_)` has a ref count of 2.
|
||||
ref_count: usize,
|
||||
},
|
||||
}
|
||||
|
||||
struct SomeExpr<'tcx> {
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
needs_unsafe_block: bool,
|
||||
}
|
||||
|
||||
// Try to parse into a recognized `Option` pattern.
|
||||
// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
|
||||
fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
|
||||
fn f<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
pat: &'tcx Pat<'_>,
|
||||
ref_count: usize,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<OptionPat<'tcx>> {
|
||||
match pat.kind {
|
||||
PatKind::Wild => Some(OptionPat::Wild),
|
||||
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
|
||||
PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
|
||||
PatKind::TupleStruct(ref qpath, [pattern], _)
|
||||
if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
|
||||
{
|
||||
Some(OptionPat::Some { pattern, ref_count })
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
f(cx, pat, 0, ctxt)
|
||||
}
|
||||
|
||||
// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
|
||||
fn get_some_expr<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
needs_unsafe_block: bool,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<SomeExpr<'tcx>> {
|
||||
// TODO: Allow more complex expressions.
|
||||
match expr.kind {
|
||||
ExprKind::Call(
|
||||
Expr {
|
||||
kind: ExprKind::Path(ref qpath),
|
||||
..
|
||||
},
|
||||
[arg],
|
||||
) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr {
|
||||
expr: arg,
|
||||
needs_unsafe_block,
|
||||
}),
|
||||
ExprKind::Block(
|
||||
Block {
|
||||
stmts: [],
|
||||
expr: Some(expr),
|
||||
rules,
|
||||
..
|
||||
},
|
||||
_,
|
||||
) => get_some_expr(
|
||||
cx,
|
||||
expr,
|
||||
needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
|
||||
ctxt,
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Checks for the `None` value.
|
||||
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
use clippy_utils::consts::constant_simple;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::usage::contains_return_break_continue_macro;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::{Arm, Expr, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::MANUAL_UNWRAP_OR;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
|
||||
let ty = cx.typeck_results().expr_ty(scrutinee);
|
||||
if_chain! {
|
||||
if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) {
|
||||
Some("Option")
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::Result) {
|
||||
Some("Result")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(or_arm) = applicable_or_arm(cx, arms);
|
||||
if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
|
||||
if let Some(indent) = indent_of(cx, expr.span);
|
||||
if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
|
||||
then {
|
||||
let reindented_or_body =
|
||||
reindent_multiline(or_body_snippet.into(), true, Some(indent));
|
||||
|
||||
let suggestion = if scrutinee.span.from_expansion() {
|
||||
// we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)`
|
||||
sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..")
|
||||
}
|
||||
else {
|
||||
sugg::Sugg::hir(cx, scrutinee, "..").maybe_par()
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_UNWRAP_OR, expr.span,
|
||||
&format!("this pattern reimplements `{}::unwrap_or`", ty_name),
|
||||
"replace with",
|
||||
format!(
|
||||
"{}.unwrap_or({})",
|
||||
suggestion,
|
||||
reindented_or_body,
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
|
||||
if_chain! {
|
||||
if arms.len() == 2;
|
||||
if arms.iter().all(|arm| arm.guard.is_none());
|
||||
if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| {
|
||||
match arm.pat.kind {
|
||||
PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
|
||||
PatKind::TupleStruct(ref qpath, [pat], _) =>
|
||||
matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr),
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
let unwrap_arm = &arms[1 - idx];
|
||||
if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind;
|
||||
if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
|
||||
if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
|
||||
if path_to_local_id(unwrap_arm.body, binding_hir_id);
|
||||
if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty();
|
||||
if !contains_return_break_continue_macro(or_arm.body);
|
||||
then {
|
||||
Some(or_arm)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
@ -24,8 +24,8 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
|
||||
let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
|
||||
if let ExprKind::Lit(ref lit) = arm_bool.kind {
|
||||
match lit.node {
|
||||
LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
|
||||
LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
|
||||
LitKind::Bool(true) => Some((arms[0].body, arms[1].body)),
|
||||
LitKind::Bool(false) => Some((arms[1].body, arms[0].body)),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_wild;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{higher, is_wild};
|
||||
use rustc_ast::{Attribute, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
|
||||
@ -11,22 +11,24 @@ use rustc_span::source_map::Spanned;
|
||||
use super::MATCH_LIKE_MATCHES_MACRO;
|
||||
|
||||
/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
|
||||
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let Some(higher::IfLet {
|
||||
let_pat,
|
||||
pub(crate) fn check_if_let<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
let_pat: &'tcx Pat<'_>,
|
||||
let_expr: &'tcx Expr<'_>,
|
||||
then_expr: &'tcx Expr<'_>,
|
||||
else_expr: &'tcx Expr<'_>,
|
||||
) {
|
||||
find_matches_sugg(
|
||||
cx,
|
||||
let_expr,
|
||||
if_then,
|
||||
if_else: Some(if_else),
|
||||
}) = higher::IfLet::hir(cx, expr)
|
||||
{
|
||||
find_matches_sugg(
|
||||
cx,
|
||||
let_expr,
|
||||
IntoIterator::into_iter([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
|
||||
expr,
|
||||
true,
|
||||
);
|
||||
}
|
||||
IntoIterator::into_iter([
|
||||
(&[][..], Some(let_pat), then_expr, None),
|
||||
(&[][..], None, else_expr, None),
|
||||
]),
|
||||
expr,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn check_match<'tcx>(
|
||||
|
@ -0,0 +1,61 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::MATCH_ON_VEC_ITEMS;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some(idx_expr) = is_vec_indexing(cx, scrutinee);
|
||||
if let ExprKind::Index(vec, idx) = idx_expr.kind;
|
||||
|
||||
then {
|
||||
// FIXME: could be improved to suggest surrounding every pattern with Some(_),
|
||||
// but only when `or_patterns` are stabilized.
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MATCH_ON_VEC_ITEMS,
|
||||
scrutinee.span,
|
||||
"indexing into a vector may panic",
|
||||
"try this",
|
||||
format!(
|
||||
"{}.get({})",
|
||||
snippet(cx, vec.span, ".."),
|
||||
snippet(cx, idx.span, "..")
|
||||
),
|
||||
Applicability::MaybeIncorrect
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if_chain! {
|
||||
if let ExprKind::Index(array, index) = expr.kind;
|
||||
if is_vector(cx, array);
|
||||
if !is_full_range(cx, index);
|
||||
|
||||
then {
|
||||
return Some(expr);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
let ty = ty.peel_refs();
|
||||
is_type_diagnostic_item(cx, ty, sym::Vec)
|
||||
}
|
||||
|
||||
fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
let ty = ty.peel_refs();
|
||||
is_type_lang_item(cx, ty, LangItem::RangeFull)
|
||||
}
|
@ -3,48 +3,13 @@ use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `match` expressions modifying the case of a string with non-compliant arms
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The arm is unreachable, which is likely a mistake
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let text = "Foo";
|
||||
///
|
||||
/// match &*text.to_ascii_lowercase() {
|
||||
/// "foo" => {},
|
||||
/// "Bar" => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let text = "Foo";
|
||||
///
|
||||
/// match &*text.to_ascii_lowercase() {
|
||||
/// "foo" => {},
|
||||
/// "bar" => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.58.0"]
|
||||
pub MATCH_STR_CASE_MISMATCH,
|
||||
correctness,
|
||||
"creation of a case altering match expression with non-compliant arms"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MatchStrCaseMismatch => [MATCH_STR_CASE_MISMATCH]);
|
||||
use super::MATCH_STR_CASE_MISMATCH;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum CaseMethod {
|
||||
@ -54,25 +19,21 @@ enum CaseMethod {
|
||||
AsciiUppercase,
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MatchStrCaseMismatch {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.tcx.sess, expr.span);
|
||||
if let ExprKind::Match(match_expr, arms, MatchSource::Normal) = expr.kind;
|
||||
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(match_expr).kind();
|
||||
if let ty::Str = ty.kind();
|
||||
then {
|
||||
let mut visitor = MatchExprVisitor {
|
||||
cx,
|
||||
case_method: None,
|
||||
};
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
|
||||
if_chain! {
|
||||
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind();
|
||||
if let ty::Str = ty.kind();
|
||||
then {
|
||||
let mut visitor = MatchExprVisitor {
|
||||
cx,
|
||||
case_method: None,
|
||||
};
|
||||
|
||||
visitor.visit_expr(match_expr);
|
||||
visitor.visit_expr(scrutinee);
|
||||
|
||||
if let Some(case_method) = visitor.case_method {
|
||||
if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) {
|
||||
lint(cx, &case_method, bad_case_span, bad_case_sym.as_str());
|
||||
}
|
||||
if let Some(case_method) = visitor.case_method {
|
||||
if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) {
|
||||
lint(cx, &case_method, bad_case_span, bad_case_sym.as_str());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +1,34 @@
|
||||
use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use clippy_utils::{higher, in_constant, meets_msrv, msrvs};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
|
||||
use rustc_lexer::{tokenize, TokenKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{Span, SpanData, SyntaxContext};
|
||||
|
||||
mod collapsible_match;
|
||||
mod infallible_destructuring_match;
|
||||
mod manual_map;
|
||||
mod manual_unwrap_or;
|
||||
mod match_as_ref;
|
||||
mod match_bool;
|
||||
mod match_like_matches;
|
||||
mod match_on_vec_items;
|
||||
mod match_ref_pats;
|
||||
mod match_same_arms;
|
||||
mod match_single_binding;
|
||||
mod match_str_case_mismatch;
|
||||
mod match_wild_enum;
|
||||
mod match_wild_err_arm;
|
||||
mod needless_match;
|
||||
mod overlapping_arms;
|
||||
mod redundant_pattern_match;
|
||||
mod rest_pat_in_fully_bound_struct;
|
||||
mod significant_drop_in_scrutinee;
|
||||
mod single_match;
|
||||
mod try_err;
|
||||
mod wild_in_or_pats;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -610,6 +618,274 @@ declare_clippy_lint! {
|
||||
"`match` or match-like `if let` that are unnecessary"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together
|
||||
/// without adding any branches.
|
||||
///
|
||||
/// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
|
||||
/// cases where merging would most likely make the code more readable.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is unnecessarily verbose and complex.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn func(opt: Option<Result<u64, String>>) {
|
||||
/// let n = match opt {
|
||||
/// Some(n) => match n {
|
||||
/// Ok(n) => n,
|
||||
/// _ => return,
|
||||
/// }
|
||||
/// None => return,
|
||||
/// };
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn func(opt: Option<Result<u64, String>>) {
|
||||
/// let n = match opt {
|
||||
/// Some(Ok(n)) => n,
|
||||
/// _ => return,
|
||||
/// };
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.50.0"]
|
||||
pub COLLAPSIBLE_MATCH,
|
||||
style,
|
||||
"Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Concise code helps focusing on behavior instead of boilerplate.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let foo: Option<i32> = None;
|
||||
/// match foo {
|
||||
/// Some(v) => v,
|
||||
/// None => 1,
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let foo: Option<i32> = None;
|
||||
/// foo.unwrap_or(1);
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub MANUAL_UNWRAP_OR,
|
||||
complexity,
|
||||
"finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `match vec[idx]` or `match vec[n..m]`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This can panic at runtime.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust, no_run
|
||||
/// let arr = vec![0, 1, 2, 3];
|
||||
/// let idx = 1;
|
||||
///
|
||||
/// // Bad
|
||||
/// match arr[idx] {
|
||||
/// 0 => println!("{}", 0),
|
||||
/// 1 => println!("{}", 3),
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust, no_run
|
||||
/// let arr = vec![0, 1, 2, 3];
|
||||
/// let idx = 1;
|
||||
///
|
||||
/// // Good
|
||||
/// match arr.get(idx) {
|
||||
/// Some(0) => println!("{}", 0),
|
||||
/// Some(1) => println!("{}", 3),
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.45.0"]
|
||||
pub MATCH_ON_VEC_ITEMS,
|
||||
pedantic,
|
||||
"matching on vector elements can panic"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `match` expressions modifying the case of a string with non-compliant arms
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The arm is unreachable, which is likely a mistake
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let text = "Foo";
|
||||
/// match &*text.to_ascii_lowercase() {
|
||||
/// "foo" => {},
|
||||
/// "Bar" => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let text = "Foo";
|
||||
/// match &*text.to_ascii_lowercase() {
|
||||
/// "foo" => {},
|
||||
/// "bar" => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.58.0"]
|
||||
pub MATCH_STR_CASE_MISMATCH,
|
||||
correctness,
|
||||
"creation of a case altering match expression with non-compliant arms"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for temporaries returned from function calls in a match scrutinee that have the
|
||||
/// `clippy::has_significant_drop` attribute.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have
|
||||
/// an important side-effect, such as unlocking a mutex, making it important for users to be
|
||||
/// able to accurately understand their lifetimes. When a temporary is returned in a function
|
||||
/// call in a match scrutinee, its lifetime lasts until the end of the match block, which may
|
||||
/// be surprising.
|
||||
///
|
||||
/// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a
|
||||
/// function call that returns a `MutexGuard` and then tries to lock again in one of the match
|
||||
/// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of
|
||||
/// the match block and thus will not unlock.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust.ignore
|
||||
/// # use std::sync::Mutex;
|
||||
///
|
||||
/// # struct State {}
|
||||
///
|
||||
/// # impl State {
|
||||
/// # fn foo(&self) -> bool {
|
||||
/// # true
|
||||
/// # }
|
||||
///
|
||||
/// # fn bar(&self) {}
|
||||
/// # }
|
||||
///
|
||||
///
|
||||
/// let mutex = Mutex::new(State {});
|
||||
///
|
||||
/// match mutex.lock().unwrap().foo() {
|
||||
/// true => {
|
||||
/// mutex.lock().unwrap().bar(); // Deadlock!
|
||||
/// }
|
||||
/// false => {}
|
||||
/// };
|
||||
///
|
||||
/// println!("All done!");
|
||||
///
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::sync::Mutex;
|
||||
///
|
||||
/// # struct State {}
|
||||
///
|
||||
/// # impl State {
|
||||
/// # fn foo(&self) -> bool {
|
||||
/// # true
|
||||
/// # }
|
||||
///
|
||||
/// # fn bar(&self) {}
|
||||
/// # }
|
||||
///
|
||||
/// let mutex = Mutex::new(State {});
|
||||
///
|
||||
/// let is_foo = mutex.lock().unwrap().foo();
|
||||
/// match is_foo {
|
||||
/// true => {
|
||||
/// mutex.lock().unwrap().bar();
|
||||
/// }
|
||||
/// false => {}
|
||||
/// };
|
||||
///
|
||||
/// println!("All done!");
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub SIGNIFICANT_DROP_IN_SCRUTINEE,
|
||||
suspicious,
|
||||
"warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usages of `Err(x)?`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The `?` operator is designed to allow calls that
|
||||
/// can fail to be easily chained. For example, `foo()?.bar()` or
|
||||
/// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will
|
||||
/// always return), it is more clear to write `return Err(x)`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn foo(fail: bool) -> Result<i32, String> {
|
||||
/// if fail {
|
||||
/// Err("failed")?;
|
||||
/// }
|
||||
/// Ok(0)
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// fn foo(fail: bool) -> Result<i32, String> {
|
||||
/// if fail {
|
||||
/// return Err("failed".into());
|
||||
/// }
|
||||
/// Ok(0)
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.38.0"]
|
||||
pub TRY_ERR,
|
||||
restriction,
|
||||
"return errors explicitly rather than hiding them behind a `?`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usages of `match` which could be implemented using `map`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using the `map` method is clearer and more concise.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// match Some(0) {
|
||||
/// Some(x) => Some(x + 1),
|
||||
/// None => None,
|
||||
/// };
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// Some(0).map(|x| x + 1);
|
||||
/// ```
|
||||
#[clippy::version = "1.52.0"]
|
||||
pub MANUAL_MAP,
|
||||
style,
|
||||
"reimplementation of `map`"
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Matches {
|
||||
msrv: Option<RustcVersion>,
|
||||
@ -644,19 +920,42 @@ impl_lint_pass!(Matches => [
|
||||
MATCH_LIKE_MATCHES_MACRO,
|
||||
MATCH_SAME_ARMS,
|
||||
NEEDLESS_MATCH,
|
||||
COLLAPSIBLE_MATCH,
|
||||
MANUAL_UNWRAP_OR,
|
||||
MATCH_ON_VEC_ITEMS,
|
||||
MATCH_STR_CASE_MISMATCH,
|
||||
SIGNIFICANT_DROP_IN_SCRUTINEE,
|
||||
TRY_ERR,
|
||||
MANUAL_MAP,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
let from_expansion = expr.span.from_expansion();
|
||||
|
||||
if let ExprKind::Match(ex, arms, source) = expr.kind {
|
||||
if !span_starts_with(cx, expr.span, "match") {
|
||||
if source == MatchSource::Normal && !span_starts_with(cx, expr.span, "match") {
|
||||
return;
|
||||
}
|
||||
if !contains_cfg_arm(cx, expr, ex, arms) {
|
||||
if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
|
||||
significant_drop_in_scrutinee::check(cx, expr, ex, source);
|
||||
}
|
||||
|
||||
collapsible_match::check_match(cx, arms);
|
||||
if !from_expansion {
|
||||
// These don't depend on a relationship between multiple arms
|
||||
match_wild_err_arm::check(cx, ex, arms);
|
||||
wild_in_or_pats::check(cx, arms);
|
||||
}
|
||||
|
||||
if source == MatchSource::TryDesugar {
|
||||
try_err::check(cx, expr, ex);
|
||||
}
|
||||
|
||||
if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) {
|
||||
if source == MatchSource::Normal {
|
||||
if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO)
|
||||
&& match_like_matches::check_match(cx, expr, ex, arms))
|
||||
@ -671,6 +970,13 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
match_wild_enum::check(cx, ex, arms);
|
||||
match_as_ref::check(cx, ex, arms, expr);
|
||||
needless_match::check_match(cx, ex, arms, expr);
|
||||
match_on_vec_items::check(cx, ex);
|
||||
match_str_case_mismatch::check(cx, ex, arms);
|
||||
|
||||
if !in_constant(cx, expr.hir_id) {
|
||||
manual_unwrap_or::check(cx, expr, ex, arms);
|
||||
manual_map::check_match(cx, expr, ex, arms);
|
||||
}
|
||||
|
||||
if self.infallible_destructuring_match_linted {
|
||||
self.infallible_destructuring_match_linted = false;
|
||||
@ -680,16 +986,35 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
}
|
||||
match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
|
||||
}
|
||||
|
||||
// These don't depend on a relationship between multiple arms
|
||||
match_wild_err_arm::check(cx, ex, arms);
|
||||
wild_in_or_pats::check(cx, arms);
|
||||
} else {
|
||||
if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) {
|
||||
match_like_matches::check(cx, expr);
|
||||
} else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
|
||||
collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else);
|
||||
if !from_expansion {
|
||||
if let Some(else_expr) = if_let.if_else {
|
||||
if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) {
|
||||
match_like_matches::check_if_let(
|
||||
cx,
|
||||
expr,
|
||||
if_let.let_pat,
|
||||
if_let.let_expr,
|
||||
if_let.if_then,
|
||||
else_expr,
|
||||
);
|
||||
}
|
||||
if !in_constant(cx, expr.hir_id) {
|
||||
manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr);
|
||||
}
|
||||
}
|
||||
redundant_pattern_match::check_if_let(
|
||||
cx,
|
||||
expr,
|
||||
if_let.let_pat,
|
||||
if_let.let_expr,
|
||||
if_let.if_else.is_some(),
|
||||
);
|
||||
needless_match::check_if_let(cx, expr, &if_let);
|
||||
}
|
||||
} else if !from_expansion {
|
||||
redundant_pattern_match::check(cx, expr);
|
||||
needless_match::check(cx, expr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,20 +47,18 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>],
|
||||
/// some_enum
|
||||
/// }
|
||||
/// ```
|
||||
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
|
||||
if let Some(ref if_let) = higher::IfLet::hir(cx, ex) {
|
||||
if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_MATCH,
|
||||
ex.span,
|
||||
"this if-let expression is unnecessary",
|
||||
"replace it with",
|
||||
snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let: &higher::IfLet<'tcx>) {
|
||||
if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let_inner(cx, if_let) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_MATCH,
|
||||
ex.span,
|
||||
"this if-let expression is unnecessary",
|
||||
"replace it with",
|
||||
snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +75,7 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>])
|
||||
true
|
||||
}
|
||||
|
||||
fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
|
||||
fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
|
||||
if let Some(if_else) = if_let.if_else {
|
||||
if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) {
|
||||
return false;
|
||||
@ -85,7 +83,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
|
||||
|
||||
// Recursively check for each `else if let` phrase,
|
||||
if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) {
|
||||
return check_if_let(cx, nested_if_let);
|
||||
return check_if_let_inner(cx, nested_if_let);
|
||||
}
|
||||
|
||||
if matches!(if_else.kind, ExprKind::Block(..)) {
|
||||
|
@ -18,19 +18,21 @@ use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
|
||||
use rustc_span::sym;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let Some(higher::IfLet {
|
||||
if_else,
|
||||
let_pat,
|
||||
let_expr,
|
||||
..
|
||||
}) = higher::IfLet::hir(cx, expr)
|
||||
{
|
||||
find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some());
|
||||
} else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
|
||||
if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
|
||||
find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_if_let<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
pat: &'tcx Pat<'_>,
|
||||
scrutinee: &'tcx Expr<'_>,
|
||||
has_else: bool,
|
||||
) {
|
||||
find_sugg_for_if_let(cx, expr, pat, scrutinee, "if", has_else);
|
||||
}
|
||||
|
||||
// Extract the generic arguments out of a type
|
||||
fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
|
||||
if_chain! {
|
||||
|
@ -4,103 +4,25 @@ use clippy_utils::get_attr;
|
||||
use clippy_utils::source::{indent_of, snippet};
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_middle::ty::{Ty, TypeAndMut};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for temporaries returned from function calls in a match scrutinee that have the
|
||||
/// `clippy::has_significant_drop` attribute.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have
|
||||
/// an important side-effect, such as unlocking a mutex, making it important for users to be
|
||||
/// able to accurately understand their lifetimes. When a temporary is returned in a function
|
||||
/// call in a match scrutinee, its lifetime lasts until the end of the match block, which may
|
||||
/// be surprising.
|
||||
///
|
||||
/// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a
|
||||
/// function call that returns a `MutexGuard` and then tries to lock again in one of the match
|
||||
/// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of
|
||||
/// the match block and thus will not unlock.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust.ignore
|
||||
/// # use std::sync::Mutex;
|
||||
///
|
||||
/// # struct State {}
|
||||
///
|
||||
/// # impl State {
|
||||
/// # fn foo(&self) -> bool {
|
||||
/// # true
|
||||
/// # }
|
||||
///
|
||||
/// # fn bar(&self) {}
|
||||
/// # }
|
||||
///
|
||||
///
|
||||
/// let mutex = Mutex::new(State {});
|
||||
///
|
||||
/// match mutex.lock().unwrap().foo() {
|
||||
/// true => {
|
||||
/// mutex.lock().unwrap().bar(); // Deadlock!
|
||||
/// }
|
||||
/// false => {}
|
||||
/// };
|
||||
///
|
||||
/// println!("All done!");
|
||||
///
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::sync::Mutex;
|
||||
///
|
||||
/// # struct State {}
|
||||
///
|
||||
/// # impl State {
|
||||
/// # fn foo(&self) -> bool {
|
||||
/// # true
|
||||
/// # }
|
||||
///
|
||||
/// # fn bar(&self) {}
|
||||
/// # }
|
||||
///
|
||||
/// let mutex = Mutex::new(State {});
|
||||
///
|
||||
/// let is_foo = mutex.lock().unwrap().foo();
|
||||
/// match is_foo {
|
||||
/// true => {
|
||||
/// mutex.lock().unwrap().bar();
|
||||
/// }
|
||||
/// false => {}
|
||||
/// };
|
||||
///
|
||||
/// println!("All done!");
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub SIGNIFICANT_DROP_IN_SCRUTINEE,
|
||||
suspicious,
|
||||
"warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime"
|
||||
}
|
||||
use super::SIGNIFICANT_DROP_IN_SCRUTINEE;
|
||||
|
||||
declare_lint_pass!(SignificantDropInScrutinee => [SIGNIFICANT_DROP_IN_SCRUTINEE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let Some(suggestions) = has_significant_drop_in_scrutinee(cx, expr) {
|
||||
for found in suggestions {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SIGNIFICANT_DROP_IN_SCRUTINEE,
|
||||
found.found_span,
|
||||
"temporary with significant drop in match scrutinee",
|
||||
|diag| set_diagnostic(diag, cx, expr, found),
|
||||
);
|
||||
}
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
scrutinee: &'tcx Expr<'_>,
|
||||
source: MatchSource,
|
||||
) {
|
||||
if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) {
|
||||
for found in suggestions {
|
||||
span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
|
||||
set_diagnostic(diag, cx, expr, found);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -152,13 +74,18 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t
|
||||
/// may have a surprising lifetime.
|
||||
fn has_significant_drop_in_scrutinee<'tcx, 'a>(
|
||||
cx: &'a LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
) -> Option<Vec<FoundSigDrop>> {
|
||||
scrutinee: &'tcx Expr<'tcx>,
|
||||
source: MatchSource,
|
||||
) -> Option<(Vec<FoundSigDrop>, &'static str)> {
|
||||
let mut helper = SigDropHelper::new(cx);
|
||||
match expr.kind {
|
||||
ExprKind::Match(match_expr, _, _) => helper.find_sig_drop(match_expr),
|
||||
_ => None,
|
||||
}
|
||||
helper.find_sig_drop(scrutinee).map(|drops| {
|
||||
let message = if source == MatchSource::Normal {
|
||||
"temporary with significant drop in match scrutinee"
|
||||
} else {
|
||||
"temporary with significant drop in for loop"
|
||||
};
|
||||
(drops, message)
|
||||
})
|
||||
}
|
||||
|
||||
struct SigDropHelper<'a, 'tcx> {
|
||||
@ -213,6 +140,19 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
|
||||
self.sig_drop_spans.take()
|
||||
}
|
||||
|
||||
fn replace_current_sig_drop(
|
||||
&mut self,
|
||||
found_span: Span,
|
||||
is_unit_return_val: bool,
|
||||
lint_suggestion: LintSuggestion,
|
||||
) {
|
||||
self.current_sig_drop.replace(FoundSigDrop {
|
||||
found_span,
|
||||
is_unit_return_val,
|
||||
lint_suggestion,
|
||||
});
|
||||
}
|
||||
|
||||
/// This will try to set the current suggestion (so it can be moved into the suggestions vec
|
||||
/// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us
|
||||
/// an opportunity to look for another type in the chain that will be trivially copyable.
|
||||
@ -229,25 +169,15 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
|
||||
// but let's avoid any chance of an ICE
|
||||
if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) {
|
||||
if ty.is_trivially_pure_clone_copy() {
|
||||
self.current_sig_drop.replace(FoundSigDrop {
|
||||
found_span: expr.span,
|
||||
is_unit_return_val: false,
|
||||
lint_suggestion: LintSuggestion::MoveAndDerefToCopy,
|
||||
});
|
||||
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndDerefToCopy);
|
||||
} else if allow_move_and_clone {
|
||||
self.current_sig_drop.replace(FoundSigDrop {
|
||||
found_span: expr.span,
|
||||
is_unit_return_val: false,
|
||||
lint_suggestion: LintSuggestion::MoveAndClone,
|
||||
});
|
||||
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
|
||||
}
|
||||
}
|
||||
} else if ty.is_trivially_pure_clone_copy() {
|
||||
self.current_sig_drop.replace(FoundSigDrop {
|
||||
found_span: expr.span,
|
||||
is_unit_return_val: false,
|
||||
lint_suggestion: LintSuggestion::MoveOnly,
|
||||
});
|
||||
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveOnly);
|
||||
} else if allow_move_and_clone {
|
||||
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,11 +209,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
|
||||
// If either side had a significant drop, suggest moving the entire scrutinee to avoid
|
||||
// unnecessary copies and to simplify cases where both sides have significant drops.
|
||||
if self.has_significant_drop {
|
||||
self.current_sig_drop.replace(FoundSigDrop {
|
||||
found_span: span,
|
||||
is_unit_return_val,
|
||||
lint_suggestion: LintSuggestion::MoveOnly,
|
||||
});
|
||||
self.replace_current_sig_drop(span, is_unit_return_val, LintSuggestion::MoveOnly);
|
||||
}
|
||||
|
||||
self.special_handling_for_binary_op = false;
|
||||
@ -363,34 +289,34 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
ExprKind::Box(..) |
|
||||
ExprKind::Array(..) |
|
||||
ExprKind::Call(..) |
|
||||
ExprKind::Unary(..) |
|
||||
ExprKind::If(..) |
|
||||
ExprKind::Match(..) |
|
||||
ExprKind::Field(..) |
|
||||
ExprKind::Index(..) |
|
||||
ExprKind::Ret(..) |
|
||||
ExprKind::Repeat(..) |
|
||||
ExprKind::Yield(..) |
|
||||
ExprKind::MethodCall(..) => walk_expr(self, ex),
|
||||
ExprKind::Array(..) |
|
||||
ExprKind::Call(..) |
|
||||
ExprKind::Unary(..) |
|
||||
ExprKind::If(..) |
|
||||
ExprKind::Match(..) |
|
||||
ExprKind::Field(..) |
|
||||
ExprKind::Index(..) |
|
||||
ExprKind::Ret(..) |
|
||||
ExprKind::Repeat(..) |
|
||||
ExprKind::Yield(..) |
|
||||
ExprKind::MethodCall(..) => walk_expr(self, ex),
|
||||
ExprKind::AddrOf(_, _, _) |
|
||||
ExprKind::Block(_, _) |
|
||||
ExprKind::Break(_, _) |
|
||||
ExprKind::Cast(_, _) |
|
||||
// Don't want to check the closure itself, only invocation, which is covered by MethodCall
|
||||
ExprKind::Closure(_, _, _, _, _) |
|
||||
ExprKind::ConstBlock(_) |
|
||||
ExprKind::Continue(_) |
|
||||
ExprKind::DropTemps(_) |
|
||||
ExprKind::Err |
|
||||
ExprKind::InlineAsm(_) |
|
||||
ExprKind::Let(_) |
|
||||
ExprKind::Lit(_) |
|
||||
ExprKind::Loop(_, _, _, _) |
|
||||
ExprKind::Path(_) |
|
||||
ExprKind::Struct(_, _, _) |
|
||||
ExprKind::Type(_, _) => {
|
||||
ExprKind::Block(_, _) |
|
||||
ExprKind::Break(_, _) |
|
||||
ExprKind::Cast(_, _) |
|
||||
// Don't want to check the closure itself, only invocation, which is covered by MethodCall
|
||||
ExprKind::Closure(_, _, _, _, _) |
|
||||
ExprKind::ConstBlock(_) |
|
||||
ExprKind::Continue(_) |
|
||||
ExprKind::DropTemps(_) |
|
||||
ExprKind::Err |
|
||||
ExprKind::InlineAsm(_) |
|
||||
ExprKind::Let(_) |
|
||||
ExprKind::Lit(_) |
|
||||
ExprKind::Loop(_, _, _, _) |
|
||||
ExprKind::Path(_) |
|
||||
ExprKind::Struct(_, _, _) |
|
||||
ExprKind::Type(_, _) => {
|
||||
return;
|
||||
}
|
||||
}
|
145
src/tools/clippy/clippy_lints/src/matches/try_err.rs
Normal file
145
src/tools/clippy/clippy_lints/src/matches/try_err.rs
Normal file
@ -0,0 +1,145 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::ResultErr;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::{hygiene, sym};
|
||||
|
||||
use super::TRY_ERR;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutinee: &'tcx Expr<'_>) {
|
||||
// Looks for a structure like this:
|
||||
// match ::std::ops::Try::into_result(Err(5)) {
|
||||
// ::std::result::Result::Err(err) =>
|
||||
// #[allow(unreachable_code)]
|
||||
// return ::std::ops::Try::from_error(::std::convert::From::from(err)),
|
||||
// ::std::result::Result::Ok(val) =>
|
||||
// #[allow(unreachable_code)]
|
||||
// val,
|
||||
// };
|
||||
if_chain! {
|
||||
if let ExprKind::Call(match_fun, try_args) = scrutinee.kind;
|
||||
if let ExprKind::Path(ref match_fun_path) = match_fun.kind;
|
||||
if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..));
|
||||
if let Some(try_arg) = try_args.get(0);
|
||||
if let ExprKind::Call(err_fun, err_args) = try_arg.kind;
|
||||
if let Some(err_arg) = err_args.get(0);
|
||||
if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
|
||||
if is_lang_ctor(cx, err_fun_path, ResultErr);
|
||||
if let Some(return_ty) = find_return_type(cx, &expr.kind);
|
||||
then {
|
||||
let prefix;
|
||||
let suffix;
|
||||
let err_ty;
|
||||
|
||||
if let Some(ty) = result_error_type(cx, return_ty) {
|
||||
prefix = "Err(";
|
||||
suffix = ")";
|
||||
err_ty = ty;
|
||||
} else if let Some(ty) = poll_result_error_type(cx, return_ty) {
|
||||
prefix = "Poll::Ready(Err(";
|
||||
suffix = "))";
|
||||
err_ty = ty;
|
||||
} else if let Some(ty) = poll_option_result_error_type(cx, return_ty) {
|
||||
prefix = "Poll::Ready(Some(Err(";
|
||||
suffix = ")))";
|
||||
err_ty = ty;
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let expr_err_ty = cx.typeck_results().expr_ty(err_arg);
|
||||
let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt());
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability);
|
||||
let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
|
||||
"" // already returns
|
||||
} else {
|
||||
"return "
|
||||
};
|
||||
let suggestion = if err_ty == expr_err_ty {
|
||||
format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix)
|
||||
} else {
|
||||
format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix)
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
TRY_ERR,
|
||||
expr.span,
|
||||
"returning an `Err(_)` with the `?` operator",
|
||||
"try this",
|
||||
suggestion,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds function return type by examining return expressions in match arms.
|
||||
fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
|
||||
if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr {
|
||||
for arm in arms.iter() {
|
||||
if let ExprKind::Ret(Some(ret)) = arm.body.kind {
|
||||
return Some(cx.typeck_results().expr_ty(ret));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Extracts the error type from Result<T, E>.
|
||||
fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let ty::Adt(_, subst) = ty.kind();
|
||||
if is_type_diagnostic_item(cx, ty, sym::Result);
|
||||
then {
|
||||
Some(subst.type_at(1))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the error type from Poll<Result<T, E>>.
|
||||
fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let ty::Adt(def, subst) = ty.kind();
|
||||
if match_def_path(cx, def.did(), &paths::POLL);
|
||||
let ready_ty = subst.type_at(0);
|
||||
|
||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
||||
if cx.tcx.is_diagnostic_item(sym::Result, ready_def.did());
|
||||
then {
|
||||
Some(ready_subst.type_at(1))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the error type from Poll<Option<Result<T, E>>>.
|
||||
fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let ty::Adt(def, subst) = ty.kind();
|
||||
if match_def_path(cx, def.did(), &paths::POLL);
|
||||
let ready_ty = subst.type_at(0);
|
||||
|
||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
||||
if cx.tcx.is_diagnostic_item(sym::Option, ready_def.did());
|
||||
let some_ty = ready_subst.type_at(0);
|
||||
|
||||
if let ty::Adt(some_def, some_subst) = some_ty.kind();
|
||||
if cx.tcx.is_diagnostic_item(sym::Result, some_def.did());
|
||||
then {
|
||||
Some(some_subst.type_at(1))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::SpanlessEq;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::GET_LAST_WITH_LEN;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
|
||||
// Argument to "get" is a subtraction
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Sub, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = arg.kind
|
||||
|
||||
// LHS of subtraction is "x.len()"
|
||||
&& let ExprKind::MethodCall(lhs_path, [lhs_recv], _) = &lhs.kind
|
||||
&& lhs_path.ident.name == sym::len
|
||||
|
||||
// RHS of subtraction is 1
|
||||
&& let ExprKind::Lit(rhs_lit) = &rhs.kind
|
||||
&& let LitKind::Int(1, ..) = rhs_lit.node
|
||||
|
||||
// check that recv == lhs_recv `recv.get(lhs_recv.len() - 1)`
|
||||
&& SpanlessEq::new(cx).eq_expr(recv, lhs_recv)
|
||||
&& !recv.can_have_side_effects()
|
||||
{
|
||||
let method = match cx.typeck_results().expr_ty_adjusted(recv).peel_refs().kind() {
|
||||
ty::Adt(def, _) if cx.tcx.is_diagnostic_item(sym::VecDeque, def.did()) => "back",
|
||||
ty::Slice(_) => "last",
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let recv_snippet = snippet_with_applicability(cx, recv.span, "_", &mut applicability);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
GET_LAST_WITH_LEN,
|
||||
expr.span,
|
||||
&format!("accessing last element with `{recv_snippet}.get({recv_snippet}.len() - 1)`"),
|
||||
"try",
|
||||
format!("{recv_snippet}.{method}()"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
@ -34,13 +34,18 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal
|
||||
if let ast::LitKind::Int(start_idx, _) = start_lit.node;
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let suggest = if start_idx == 0 {
|
||||
format!("{}.first()", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability))
|
||||
} else {
|
||||
format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx)
|
||||
};
|
||||
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),
|
||||
suggest,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
@ -55,7 +60,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal
|
||||
"using `.iter().next()` on an array",
|
||||
"try calling",
|
||||
format!(
|
||||
"{}.get(0)",
|
||||
"{}.first()",
|
||||
snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
|
@ -21,6 +21,7 @@ mod filter_next;
|
||||
mod flat_map_identity;
|
||||
mod flat_map_option;
|
||||
mod from_iter_instead_of_collect;
|
||||
mod get_last_with_len;
|
||||
mod get_unwrap;
|
||||
mod implicit_clone;
|
||||
mod inefficient_to_string;
|
||||
@ -44,6 +45,7 @@ mod map_identity;
|
||||
mod map_unwrap_or;
|
||||
mod needless_option_as_deref;
|
||||
mod needless_option_take;
|
||||
mod no_effect_replace;
|
||||
mod ok_expect;
|
||||
mod option_as_ref_deref;
|
||||
mod option_map_or_none;
|
||||
@ -192,25 +194,18 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rust
|
||||
/// # let opt = Some(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// opt.unwrap();
|
||||
///
|
||||
/// // Good
|
||||
/// opt.expect("more helpful message");
|
||||
/// # let option = Some(1);
|
||||
/// # let result: Result<usize, ()> = Ok(1);
|
||||
/// option.unwrap();
|
||||
/// result.unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let res: Result<usize, ()> = Ok(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// res.unwrap();
|
||||
///
|
||||
/// // Good
|
||||
/// res.expect("more helpful message");
|
||||
/// # let option = Some(1);
|
||||
/// # let result: Result<usize, ()> = Ok(1);
|
||||
/// option.expect("more helpful message");
|
||||
/// result.expect("more helpful message");
|
||||
/// ```
|
||||
#[clippy::version = "1.45.0"]
|
||||
pub UNWRAP_USED,
|
||||
@ -233,27 +228,21 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rust,ignore
|
||||
/// # let opt = Some(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// opt.expect("one");
|
||||
///
|
||||
/// // Good
|
||||
/// let opt = Some(1);
|
||||
/// opt?;
|
||||
/// # let option = Some(1);
|
||||
/// # let result: Result<usize, ()> = Ok(1);
|
||||
/// option.expect("one");
|
||||
/// result.expect("one");
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// # let option = Some(1);
|
||||
/// # let result: Result<usize, ()> = Ok(1);
|
||||
/// option?;
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// ```rust
|
||||
/// # let res: Result<usize, ()> = Ok(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// res.expect("one");
|
||||
///
|
||||
/// // Good
|
||||
/// res?;
|
||||
/// # Ok::<(), ()>(())
|
||||
/// result?;
|
||||
/// ```
|
||||
#[clippy::version = "1.45.0"]
|
||||
pub EXPECT_USED,
|
||||
@ -429,26 +418,20 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rust
|
||||
/// # let x = Some(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// x.map(|a| a + 1).unwrap_or(0);
|
||||
///
|
||||
/// // Good
|
||||
/// x.map_or(0, |a| a + 1);
|
||||
/// # let option = Some(1);
|
||||
/// # let result: Result<usize, ()> = Ok(1);
|
||||
/// # fn some_function(foo: ()) -> usize { 1 }
|
||||
/// option.map(|a| a + 1).unwrap_or(0);
|
||||
/// result.map(|a| a + 1).unwrap_or_else(some_function);
|
||||
/// ```
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x: Result<usize, ()> = Ok(1);
|
||||
/// # let option = Some(1);
|
||||
/// # let result: Result<usize, ()> = Ok(1);
|
||||
/// # fn some_function(foo: ()) -> usize { 1 }
|
||||
///
|
||||
/// // Bad
|
||||
/// x.map(|a| a + 1).unwrap_or_else(some_function);
|
||||
///
|
||||
/// // Good
|
||||
/// x.map_or_else(some_function, |a| a + 1);
|
||||
/// option.map_or(0, |a| a + 1);
|
||||
/// result.map_or_else(some_function, |a| a + 1);
|
||||
/// ```
|
||||
#[clippy::version = "1.45.0"]
|
||||
pub MAP_UNWRAP_OR,
|
||||
@ -791,13 +774,14 @@ declare_clippy_lint! {
|
||||
/// # let foo = Some(String::new());
|
||||
/// foo.unwrap_or(String::new());
|
||||
/// ```
|
||||
/// this can instead be written:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let foo = Some(String::new());
|
||||
/// foo.unwrap_or_else(String::new);
|
||||
/// ```
|
||||
/// or
|
||||
/// ```rust
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// # let foo = Some(String::new());
|
||||
/// foo.unwrap_or_default();
|
||||
/// ```
|
||||
@ -861,15 +845,14 @@ declare_clippy_lint! {
|
||||
/// # let err_code = "418";
|
||||
/// # let err_msg = "I'm a teapot";
|
||||
/// foo.expect(&format!("Err {}: {}", err_code, err_msg));
|
||||
/// ```
|
||||
/// or
|
||||
/// ```rust
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// # let foo = Some(String::new());
|
||||
/// # let err_code = "418";
|
||||
/// # let err_msg = "I'm a teapot";
|
||||
/// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
|
||||
/// ```
|
||||
/// this can instead be written:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let foo = Some(String::new());
|
||||
/// # let err_code = "418";
|
||||
@ -1209,6 +1192,38 @@ declare_clippy_lint! {
|
||||
"replace `.drain(..)` with `.into_iter()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for using `x.get(x.len() - 1)` instead of
|
||||
/// `x.last()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using `x.last()` is easier to read and has the same
|
||||
/// result.
|
||||
///
|
||||
/// Note that using `x[x.len() - 1]` is semantically different from
|
||||
/// `x.last()`. Indexing into the array will panic on out-of-bounds
|
||||
/// accesses, while `x.get()` and `x.last()` will return `None`.
|
||||
///
|
||||
/// There is another lint (get_unwrap) that covers the case of using
|
||||
/// `x.get(index).unwrap()` instead of `x[index]`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let last_element = x.get(x.len() - 1);
|
||||
///
|
||||
/// // Good
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let last_element = x.last();
|
||||
/// ```
|
||||
#[clippy::version = "1.37.0"]
|
||||
pub GET_LAST_WITH_LEN,
|
||||
complexity,
|
||||
"Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for use of `.get().unwrap()` (or
|
||||
@ -2195,6 +2210,24 @@ declare_clippy_lint! {
|
||||
"using `.as_ref().take()` on a temporary value"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `replace` statements which have no effect.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's either a mistake or confusing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// "1234".replace("12", "12");
|
||||
/// "1234".replacen("12", "12", 1);
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub NO_EFFECT_REPLACE,
|
||||
suspicious,
|
||||
"replace with no effect"
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Option<RustcVersion>,
|
||||
@ -2264,6 +2297,7 @@ impl_lint_pass!(Methods => [
|
||||
BYTES_NTH,
|
||||
ITER_SKIP_NEXT,
|
||||
GET_UNWRAP,
|
||||
GET_LAST_WITH_LEN,
|
||||
STRING_EXTEND_CHARS,
|
||||
ITER_CLONED_COLLECT,
|
||||
ITER_WITH_DRAIN,
|
||||
@ -2294,6 +2328,7 @@ impl_lint_pass!(Methods => [
|
||||
NEEDLESS_OPTION_AS_DEREF,
|
||||
IS_DIGIT_ASCII_RADIX,
|
||||
NEEDLESS_OPTION_TAKE,
|
||||
NO_EFFECT_REPLACE,
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
@ -2590,6 +2625,7 @@ impl Methods {
|
||||
inspect_for_each::check(cx, expr, span2);
|
||||
}
|
||||
},
|
||||
("get", [arg]) => get_last_with_len::check(cx, expr, recv, arg),
|
||||
("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
|
||||
("is_file", []) => filetype_is_file::check(cx, expr, recv),
|
||||
("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
|
||||
@ -2705,6 +2741,9 @@ impl Methods {
|
||||
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
|
||||
},
|
||||
},
|
||||
("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => {
|
||||
no_effect_replace::check(cx, expr, arg1, arg2);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::SpanlessEq;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_hir::ExprKind;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::NO_EFFECT_REPLACE;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx rustc_hir::Expr<'_>,
|
||||
arg1: &'tcx rustc_hir::Expr<'_>,
|
||||
arg2: &'tcx rustc_hir::Expr<'_>,
|
||||
) {
|
||||
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
|
||||
if !(ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(spanned) = &arg1.kind;
|
||||
if let Some(param1) = lit_string_value(&spanned.node);
|
||||
|
||||
if let ExprKind::Lit(spanned) = &arg2.kind;
|
||||
if let LitKind::Str(param2, _) = &spanned.node;
|
||||
if param1 == param2.as_str();
|
||||
|
||||
then {
|
||||
span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself");
|
||||
}
|
||||
}
|
||||
|
||||
if SpanlessEq::new(cx).eq_expr(arg1, arg2) {
|
||||
span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself");
|
||||
}
|
||||
}
|
||||
|
||||
fn lit_string_value(node: &LitKind) -> Option<String> {
|
||||
match node {
|
||||
LitKind::Char(value) => Some(value.to_string()),
|
||||
LitKind::Str(value, _) => Some(value.as_str().to_owned()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
@ -97,7 +97,7 @@ pub(super) fn check<'tcx>(
|
||||
let func_snippet = snippet(cx, map_arg.span, "..");
|
||||
let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
|
||||
`and_then(..)` instead";
|
||||
return span_lint_and_sugg(
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
OPTION_MAP_OR_NONE,
|
||||
expr.span,
|
||||
@ -110,7 +110,7 @@ pub(super) fn check<'tcx>(
|
||||
let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
|
||||
`ok()` instead";
|
||||
let self_snippet = snippet(cx, recv.span, "..");
|
||||
return span_lint_and_sugg(
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
RESULT_MAP_OR_INTO_OPTION,
|
||||
expr.span,
|
||||
|
@ -415,7 +415,7 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id:
|
||||
/// Returns true if the named method can be used to convert the receiver to its "owned"
|
||||
/// representation.
|
||||
fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
|
||||
is_clone_like(cx, &*method_name.as_str(), method_def_id)
|
||||
is_clone_like(cx, method_name.as_str(), method_def_id)
|
||||
|| is_cow_into_owned(cx, method_name, method_def_id)
|
||||
|| is_to_string(cx, method_name, method_def_id)
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ declare_clippy_lint! {
|
||||
/// the least it hurts readability of the code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// ```rust,ignore
|
||||
/// min(0, max(100, x))
|
||||
/// ```
|
||||
/// or
|
||||
/// ```ignore
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// x.max(100).min(0)
|
||||
/// ```
|
||||
/// It will always be equal to `0`. Probably the author meant to clamp the value
|
||||
|
@ -103,11 +103,14 @@ declare_clippy_lint! {
|
||||
/// let x = 1.2331f64;
|
||||
/// let y = 1.2332f64;
|
||||
///
|
||||
/// // Bad
|
||||
/// if y == 1.23f64 { }
|
||||
/// if y != x {} // where both are floats
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = 1.2331f64;
|
||||
/// # let y = 1.2332f64;
|
||||
/// let error_margin = f64::EPSILON; // Use an epsilon for comparison
|
||||
/// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
|
||||
/// // let error_margin = std::f64::EPSILON;
|
||||
@ -258,10 +261,13 @@ declare_clippy_lint! {
|
||||
/// let x: f64 = 1.0;
|
||||
/// const ONE: f64 = 1.00;
|
||||
///
|
||||
/// // Bad
|
||||
/// if x == ONE { } // where both are floats
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x: f64 = 1.0;
|
||||
/// # const ONE: f64 = 1.00;
|
||||
/// let error_margin = f64::EPSILON; // Use an epsilon for comparison
|
||||
/// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
|
||||
/// // let error_margin = std::f64::EPSILON;
|
||||
|
@ -0,0 +1,116 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{GenericArg, Item, ItemKind, QPath, Ty, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::GenericParamDefKind;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for type parameters which are positioned inconsistently between
|
||||
/// a type definition and impl block. Specifically, a paramater in an impl
|
||||
/// block which has the same name as a parameter in the type def, but is in
|
||||
/// a different place.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Type parameters are determined by their position rather than name.
|
||||
/// Naming type parameters inconsistently may cause you to refer to the
|
||||
/// wrong type parameter.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// struct Foo<A, B> {
|
||||
/// x: A,
|
||||
/// y: B,
|
||||
/// }
|
||||
/// // inside the impl, B refers to Foo::A
|
||||
/// impl<B, A> Foo<B, A> {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct Foo<A, B> {
|
||||
/// x: A,
|
||||
/// y: B,
|
||||
/// }
|
||||
/// impl<A, B> Foo<A, B> {}
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub MISMATCHING_TYPE_PARAM_ORDER,
|
||||
pedantic,
|
||||
"type parameter positioned inconsistently between type def and impl block"
|
||||
}
|
||||
declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if_chain! {
|
||||
if !item.span.from_expansion();
|
||||
if let ItemKind::Impl(imp) = &item.kind;
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind;
|
||||
if let Some(segment) = path.segments.iter().next();
|
||||
if let Some(generic_args) = segment.args;
|
||||
if !generic_args.args.is_empty();
|
||||
then {
|
||||
// get the name and span of the generic parameters in the Impl
|
||||
let impl_params = generic_args.args.iter()
|
||||
.filter_map(|p|
|
||||
match p {
|
||||
GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
|
||||
Some((path.segments[0].ident.to_string(), path.span)),
|
||||
_ => None,
|
||||
}
|
||||
);
|
||||
|
||||
// find the type that the Impl is for
|
||||
// only lint on struct/enum/union for now
|
||||
let defid = match path.res {
|
||||
Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// get the names of the generic parameters in the type
|
||||
let type_params = &cx.tcx.generics_of(defid).params;
|
||||
let type_param_names: Vec<_> = type_params.iter()
|
||||
.filter_map(|p|
|
||||
match p.kind {
|
||||
GenericParamDefKind::Type {..} => Some(p.name.to_string()),
|
||||
_ => None,
|
||||
}
|
||||
).collect();
|
||||
// hashmap of name -> index for mismatch_param_name
|
||||
let type_param_names_hashmap: FxHashMap<&String, usize> =
|
||||
type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect();
|
||||
|
||||
let type_name = segment.ident;
|
||||
for (i, (impl_param_name, impl_param_span)) in impl_params.enumerate() {
|
||||
if mismatch_param_name(i, &impl_param_name, &type_param_names_hashmap) {
|
||||
let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order",
|
||||
type_name, impl_param_name);
|
||||
let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params",
|
||||
type_param_names[i], type_name);
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
MISMATCHING_TYPE_PARAM_ORDER,
|
||||
impl_param_span,
|
||||
&msg,
|
||||
None,
|
||||
&help
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if impl_param_name is the same as one of type_param_names,
|
||||
// and is in a different position
|
||||
fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool {
|
||||
if let Some(j) = type_param_names.get(impl_param_name) {
|
||||
if i != *j {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
@ -120,7 +120,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
|
||||
self.visit_expr(if_expr);
|
||||
}
|
||||
// make sure top level arm expressions aren't linted
|
||||
self.maybe_walk_expr(&*arm.body);
|
||||
self.maybe_walk_expr(arm.body);
|
||||
}
|
||||
},
|
||||
_ => walk_expr(self, e),
|
||||
|
@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
|
||||
if let hir::PatKind::Wild = local.pat.kind {
|
||||
return;
|
||||
}
|
||||
check_ty(cx, local.span, cx.typeck_results().pat_ty(&*local.pat));
|
||||
check_ty(cx, local.span, cx.typeck_results().pat_ty(local.pat));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,12 @@ declare_clippy_lint! {
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// debug_assert_eq!(vec![3].pop(), Some(3));
|
||||
///
|
||||
/// // or
|
||||
/// fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }
|
||||
/// debug_assert!(take_a_mut_parameter(&mut 5));
|
||||
///
|
||||
/// # let mut x = 5;
|
||||
/// # fn takes_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }
|
||||
/// debug_assert!(takes_a_mut_parameter(&mut x));
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub DEBUG_ASSERT_WITH_MUT_CALL,
|
||||
|
@ -194,14 +194,15 @@ fn assignment_suggestions<'tcx>(
|
||||
}))
|
||||
.collect::<Option<Vec<(Span, String)>>>()?;
|
||||
|
||||
let applicability = if suggestions.len() > 1 {
|
||||
match suggestions.len() {
|
||||
// All of `exprs` are never types
|
||||
// https://github.com/rust-lang/rust-clippy/issues/8911
|
||||
0 => None,
|
||||
1 => Some((Applicability::MachineApplicable, suggestions)),
|
||||
// multiple suggestions don't work with rustfix in multipart_suggest
|
||||
// https://github.com/rust-lang/rustfix/issues/141
|
||||
Applicability::Unspecified
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
Some((applicability, suggestions))
|
||||
_ => Some((Applicability::Unspecified, suggestions)),
|
||||
}
|
||||
}
|
||||
|
||||
struct Usage<'tcx> {
|
||||
|
@ -51,16 +51,16 @@ declare_clippy_lint! {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct Arithmetic {
|
||||
pub struct NumericArithmetic {
|
||||
expr_span: Option<Span>,
|
||||
/// This field is used to check whether expressions are constants, such as in enum discriminants
|
||||
/// and consts
|
||||
const_span: Option<Span>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(Arithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]);
|
||||
impl_lint_pass!(NumericArithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Arithmetic {
|
||||
impl<'tcx> LateLintPass<'tcx> for NumericArithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if self.expr_span.is_some() {
|
||||
return;
|
@ -61,7 +61,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir
|
||||
expr_visitor_no_bodies(|expr| {
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true };
|
||||
if matches!(
|
||||
&*cx.tcx.item_name(macro_call.def_id).as_str(),
|
||||
cx.tcx.item_name(macro_call.def_id).as_str(),
|
||||
"unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne"
|
||||
) {
|
||||
panics.push(macro_call.span);
|
||||
|
@ -233,7 +233,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
|
||||
}
|
||||
|
||||
if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind {
|
||||
self.check_poly_fn(cx, item.def_id, &*method_sig.decl, None);
|
||||
self.check_poly_fn(cx, item.def_id, method_sig.decl, None);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
|
||||
},
|
||||
ExprKind::Binary(ref op, l, r) => {
|
||||
if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr);
|
||||
check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
@ -213,12 +213,12 @@ fn check_possible_range_contains(
|
||||
left: &Expr<'_>,
|
||||
right: &Expr<'_>,
|
||||
expr: &Expr<'_>,
|
||||
span: Span,
|
||||
) {
|
||||
if in_constant(cx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let span = expr.span;
|
||||
let combine_and = match op {
|
||||
BinOpKind::And | BinOpKind::BitAnd => true,
|
||||
BinOpKind::Or | BinOpKind::BitOr => false,
|
||||
@ -294,6 +294,20 @@ fn check_possible_range_contains(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// If the LHS is the same operator, we have to recurse to get the "real" RHS, since they have
|
||||
// the same operator precedence
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind;
|
||||
if op == lhs_op.node;
|
||||
let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent());
|
||||
if let Some(snip) = &snippet_opt(cx, new_span);
|
||||
// Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong
|
||||
if snip.matches('(').count() == snip.matches(')').count();
|
||||
then {
|
||||
check_possible_range_contains(cx, op, new_lhs, right, expr, new_span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RangeBounds<'a> {
|
||||
|
@ -2,7 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher::VecArgs;
|
||||
use clippy_utils::last_path_segment;
|
||||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use clippy_utils::paths;
|
||||
use clippy_utils::source::{indent_of, snippet};
|
||||
use clippy_utils::ty::match_type;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
@ -11,10 +13,11 @@ use rustc_span::{sym, Span, Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `Arc::new` or `Rc::new` in `vec![elem; len]`
|
||||
/// Checks for reference-counted pointers (`Arc`, `Rc`, `rc::Weak`, and `sync::Weak`)
|
||||
/// in `vec![elem; len]`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This will create `elem` once and clone it `len` times - doing so with `Arc` or `Rc`
|
||||
/// This will create `elem` once and clone it `len` times - doing so with `Arc`/`Rc`/`Weak`
|
||||
/// is a bit misleading, as it will create references to the same pointer, rather
|
||||
/// than different instances.
|
||||
///
|
||||
@ -26,7 +29,6 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
///
|
||||
/// // Initialize each value separately:
|
||||
/// let mut data = Vec::with_capacity(100);
|
||||
/// for _ in 0..100 {
|
||||
@ -42,7 +44,7 @@ declare_clippy_lint! {
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub RC_CLONE_IN_VEC_INIT,
|
||||
suspicious,
|
||||
"initializing `Arc` or `Rc` in `vec![elem; len]`"
|
||||
"initializing reference-counted pointer in `vec![elem; len]`"
|
||||
}
|
||||
declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]);
|
||||
|
||||
@ -50,26 +52,12 @@ impl LateLintPass<'_> for RcCloneInVecInit {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; };
|
||||
let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; };
|
||||
let Some(symbol) = new_reference_call(cx, elem) else { return; };
|
||||
let Some((symbol, func_span)) = ref_init(cx, elem) else { return; };
|
||||
|
||||
emit_lint(cx, symbol, macro_call.span, elem, len);
|
||||
emit_lint(cx, symbol, macro_call.span, elem, len, func_span);
|
||||
}
|
||||
}
|
||||
|
||||
fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String {
|
||||
let elem_snippet = snippet(cx, elem.span, "..").to_string();
|
||||
if elem_snippet.contains('\n') {
|
||||
// This string must be found in `elem_snippet`, otherwise we won't be constructing
|
||||
// the snippet in the first place.
|
||||
let reference_creation = format!("{symbol_name}::new");
|
||||
let (code_until_reference_creation, _right) = elem_snippet.split_once(&reference_creation).unwrap();
|
||||
|
||||
return format!("{code_until_reference_creation}{reference_creation}(..)");
|
||||
}
|
||||
|
||||
elem_snippet
|
||||
}
|
||||
|
||||
fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String {
|
||||
format!(
|
||||
r#"{{
|
||||
@ -89,17 +77,17 @@ fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>) {
|
||||
fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>, func_span: Span) {
|
||||
let symbol_name = symbol.as_str();
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
RC_CLONE_IN_VEC_INIT,
|
||||
lint_span,
|
||||
&format!("calling `{symbol_name}::new` in `vec![elem; len]`"),
|
||||
"initializing a reference-counted pointer in `vec![elem; len]`",
|
||||
|diag| {
|
||||
let len_snippet = snippet(cx, len.span, "..");
|
||||
let elem_snippet = elem_snippet(cx, elem, symbol_name);
|
||||
let elem_snippet = format!("{}(..)", snippet(cx, elem.span.with_hi(func_span.hi()), ".."));
|
||||
let indentation = " ".repeat(indent_of(cx, lint_span).unwrap_or(0));
|
||||
let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation);
|
||||
let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation);
|
||||
@ -109,7 +97,7 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<
|
||||
lint_span,
|
||||
format!("consider initializing each `{symbol_name}` element individually"),
|
||||
loop_init_suggestion,
|
||||
Applicability::Unspecified,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
diag.span_suggestion(
|
||||
lint_span,
|
||||
@ -117,23 +105,33 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<
|
||||
"or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable"
|
||||
),
|
||||
extract_suggestion,
|
||||
Applicability::Unspecified,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Checks whether the given `expr` is a call to `Arc::new` or `Rc::new`
|
||||
fn new_reference_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
|
||||
/// Checks whether the given `expr` is a call to `Arc::new`, `Rc::new`, or evaluates to a `Weak`
|
||||
fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, Span)> {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, _args) = expr.kind;
|
||||
if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind;
|
||||
if let TyKind::Path(ref ty_path) = ty.kind;
|
||||
if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id();
|
||||
if last_path_segment(func_path).ident.name == sym::new;
|
||||
|
||||
then {
|
||||
return cx.tcx.get_diagnostic_name(def_id).filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc);
|
||||
if last_path_segment(func_path).ident.name == sym::new
|
||||
&& let Some(symbol) = cx
|
||||
.tcx
|
||||
.get_diagnostic_name(def_id)
|
||||
.filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc) {
|
||||
return Some((symbol, func.span));
|
||||
}
|
||||
|
||||
let ty_path = cx.typeck_results().expr_ty(expr);
|
||||
if match_type(cx, ty_path, &paths::WEAK_RC) || match_type(cx, ty_path, &paths::WEAK_ARC) {
|
||||
return Some((Symbol::intern("Weak"), func.span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,8 +288,8 @@ fn is_call_with_ref_arg<'tcx>(
|
||||
if let mir::TerminatorKind::Call { func, args, destination, .. } = kind;
|
||||
if args.len() == 1;
|
||||
if let mir::Operand::Move(mir::Place { local, .. }) = &args[0];
|
||||
if let ty::FnDef(def_id, _) = *func.ty(&*mir, cx.tcx).kind();
|
||||
if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
|
||||
if let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind();
|
||||
if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(mir, cx.tcx));
|
||||
if !is_copy(cx, inner_ty);
|
||||
then {
|
||||
Some((def_id, *local, inner_ty, destination.as_local()?))
|
||||
@ -318,7 +318,7 @@ fn find_stmt_assigns_to<'tcx>(
|
||||
None
|
||||
})?;
|
||||
|
||||
match (by_ref, &*rvalue) {
|
||||
match (by_ref, rvalue) {
|
||||
(true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => {
|
||||
Some(base_local_and_movability(cx, mir, *place))
|
||||
},
|
||||
|
@ -51,12 +51,12 @@ impl RedundantStaticLifetimes {
|
||||
fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
|
||||
match ty.kind {
|
||||
// Be careful of nested structures (arrays and tuples)
|
||||
TyKind::Array(ref ty, _) => {
|
||||
self.visit_type(&*ty, cx, reason);
|
||||
TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => {
|
||||
self.visit_type(ty, cx, reason);
|
||||
},
|
||||
TyKind::Tup(ref tup) => {
|
||||
for tup_ty in tup {
|
||||
self.visit_type(&*tup_ty, cx, reason);
|
||||
self.visit_type(tup_ty, cx, reason);
|
||||
}
|
||||
},
|
||||
// This is what we are looking for !
|
||||
@ -89,9 +89,6 @@ impl RedundantStaticLifetimes {
|
||||
}
|
||||
self.visit_type(&*borrow_type.ty, cx, reason);
|
||||
},
|
||||
TyKind::Slice(ref ty) => {
|
||||
self.visit_type(ty, cx, reason);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::source::{snippet_opt, snippet_with_context};
|
||||
use clippy_utils::{fn_def_id, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::Attribute;
|
||||
@ -226,14 +226,10 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option<Spa
|
||||
}
|
||||
match inner_span {
|
||||
Some(inner_span) => {
|
||||
if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
|
||||
if let Some(snippet) = snippet_opt(cx, inner_span) {
|
||||
diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
|
||||
}
|
||||
let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
|
||||
diag.span_suggestion(ret_span, "remove `return`", snippet, applicability);
|
||||
});
|
||||
},
|
||||
None => match replacement {
|
||||
@ -287,7 +283,7 @@ struct BorrowVisitor<'a, 'tcx> {
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if self.borrows {
|
||||
if self.borrows || expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,7 @@ use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::hir_id::ItemLocalId;
|
||||
use rustc_hir::{
|
||||
Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp,
|
||||
};
|
||||
use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{Span, Symbol};
|
||||
@ -141,31 +139,34 @@ impl<'tcx> LateLintPass<'tcx> for Shadow {
|
||||
|
||||
fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
|
||||
let hir = cx.tcx.hir();
|
||||
if !matches!(hir.body_owner_kind(hir.body_owner_def_id(body.id())), BodyOwnerKind::Closure)
|
||||
{
|
||||
if !matches!(
|
||||
hir.body_owner_kind(hir.body_owner_def_id(body.id())),
|
||||
BodyOwnerKind::Closure
|
||||
) {
|
||||
self.bindings.push(FxHashMap::default());
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
|
||||
let hir = cx.tcx.hir();
|
||||
if !matches!(hir.body_owner_kind(hir.body_owner_def_id(body.id())), BodyOwnerKind::Closure)
|
||||
{
|
||||
if !matches!(
|
||||
hir.body_owner_kind(hir.body_owner_def_id(body.id())),
|
||||
BodyOwnerKind::Closure
|
||||
) {
|
||||
self.bindings.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_shadow(
|
||||
cx: &LateContext<'_>,
|
||||
owner: LocalDefId,
|
||||
first: ItemLocalId,
|
||||
second: ItemLocalId,
|
||||
) -> bool {
|
||||
let scope_tree = cx.tcx.region_scope_tree(owner);
|
||||
let first_scope = scope_tree.var_scope(first).unwrap();
|
||||
let second_scope = scope_tree.var_scope(second).unwrap();
|
||||
scope_tree.is_subscope_of(second_scope, first_scope)
|
||||
fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool {
|
||||
let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id());
|
||||
if let Some(first_scope) = scope_tree.var_scope(first) {
|
||||
if let Some(second_scope) = scope_tree.var_scope(second) {
|
||||
return scope_tree.is_subscope_of(second_scope, first_scope);
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) {
|
||||
@ -177,16 +178,15 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span)
|
||||
snippet(cx, expr.span, "..")
|
||||
);
|
||||
(SHADOW_SAME, msg)
|
||||
}
|
||||
},
|
||||
Some(expr) if is_local_used(cx, expr, shadowed) => {
|
||||
let msg = format!("`{}` is shadowed", snippet(cx, pat.span, "_"));
|
||||
(SHADOW_REUSE, msg)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
let msg =
|
||||
format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_"));
|
||||
let msg = format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_"));
|
||||
(SHADOW_UNRELATED, msg)
|
||||
}
|
||||
},
|
||||
};
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
@ -215,7 +215,14 @@ fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_
|
||||
expr = match expr.kind {
|
||||
ExprKind::Box(e)
|
||||
| ExprKind::AddrOf(_, _, e)
|
||||
| ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _)
|
||||
| ExprKind::Block(
|
||||
&Block {
|
||||
stmts: [],
|
||||
expr: Some(e),
|
||||
..
|
||||
},
|
||||
_,
|
||||
)
|
||||
| ExprKind::Unary(UnOp::Deref, e) => e,
|
||||
ExprKind::Path(QPath::Resolved(None, path)) => break path.res == Res::Local(hir_id),
|
||||
_ => break false,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user