mirror of
https://github.com/rust-lang/rust-clippy
synced 2025-02-17 06:28:42 +00:00
Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
c200dad300
124 changed files with 3808 additions and 1162 deletions
|
@ -5249,6 +5249,7 @@ Released 2018-09-13
|
||||||
[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
|
[`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
|
[`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
|
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
|
||||||
|
[`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
|
||||||
[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
|
[`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
|
[`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_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
|
||||||
|
@ -5447,6 +5448,7 @@ Released 2018-09-13
|
||||||
[`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes
|
[`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes
|
||||||
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
|
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
|
||||||
[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
|
[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
|
||||||
|
[`macro_metavars_in_unsafe`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe
|
||||||
[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
|
[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
|
||||||
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
|
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
|
||||||
[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
|
[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
|
||||||
|
@ -5702,6 +5704,7 @@ Released 2018-09-13
|
||||||
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
|
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
|
||||||
[`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns
|
[`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns
|
||||||
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
|
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
|
||||||
|
[`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params
|
||||||
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
|
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
|
||||||
[`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity
|
[`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity
|
||||||
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
|
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
|
||||||
|
@ -5941,6 +5944,7 @@ Released 2018-09-13
|
||||||
[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
|
[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
|
||||||
[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests
|
[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests
|
||||||
[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception
|
[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception
|
||||||
|
[`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for
|
||||||
[`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests
|
[`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests
|
||||||
[`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests
|
[`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests
|
||||||
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
|
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
|
||||||
|
@ -6002,4 +6006,5 @@ Released 2018-09-13
|
||||||
[`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold
|
[`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold
|
||||||
[`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold
|
[`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold
|
||||||
[`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports
|
[`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports
|
||||||
|
[`warn-unsafe-macro-metavars-in-private-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-unsafe-macro-metavars-in-private-macros
|
||||||
<!-- end autogenerated links to configuration documentation -->
|
<!-- end autogenerated links to configuration documentation -->
|
||||||
|
|
|
@ -122,6 +122,28 @@ Whether to allow module inception if it's not public.
|
||||||
* [`module_inception`](https://rust-lang.github.io/rust-clippy/master/index.html#module_inception)
|
* [`module_inception`](https://rust-lang.github.io/rust-clippy/master/index.html#module_inception)
|
||||||
|
|
||||||
|
|
||||||
|
## `allow-renamed-params-for`
|
||||||
|
List of trait paths to ignore when checking renamed function parameters.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```toml
|
||||||
|
allow-renamed-params-for = [ "std::convert::From" ]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Noteworthy
|
||||||
|
|
||||||
|
- By default, the following traits are ignored: `From`, `TryFrom`, `FromStr`
|
||||||
|
- `".."` can be used as part of the list to indicate that the configured values should be appended to the
|
||||||
|
default configuration of Clippy. By default, any configuration will replace the default value.
|
||||||
|
|
||||||
|
**Default Value:** `["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"]`
|
||||||
|
|
||||||
|
---
|
||||||
|
**Affected lints:**
|
||||||
|
* [`renamed_function_params`](https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params)
|
||||||
|
|
||||||
|
|
||||||
## `allow-unwrap-in-tests`
|
## `allow-unwrap-in-tests`
|
||||||
Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
|
Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
|
||||||
|
|
||||||
|
@ -900,3 +922,13 @@ Whether to allow certain wildcard imports (prelude, super in tests).
|
||||||
* [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports)
|
* [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports)
|
||||||
|
|
||||||
|
|
||||||
|
## `warn-unsafe-macro-metavars-in-private-macros`
|
||||||
|
Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros.
|
||||||
|
|
||||||
|
**Default Value:** `false`
|
||||||
|
|
||||||
|
---
|
||||||
|
**Affected lints:**
|
||||||
|
* [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,8 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
|
||||||
const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
|
const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
|
||||||
const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"];
|
const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"];
|
||||||
const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"];
|
const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"];
|
||||||
|
const DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] =
|
||||||
|
&["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"];
|
||||||
|
|
||||||
/// Conf with parse errors
|
/// Conf with parse errors
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -613,6 +615,27 @@ define_Conf! {
|
||||||
/// - Use `".."` as part of the list to indicate that the configured values should be appended to the
|
/// - Use `".."` as part of the list to indicate that the configured values should be appended to the
|
||||||
/// default configuration of Clippy. By default, any configuration will replace the default value
|
/// default configuration of Clippy. By default, any configuration will replace the default value
|
||||||
(allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()),
|
(allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()),
|
||||||
|
/// Lint: RENAMED_FUNCTION_PARAMS.
|
||||||
|
///
|
||||||
|
/// List of trait paths to ignore when checking renamed function parameters.
|
||||||
|
///
|
||||||
|
/// #### Example
|
||||||
|
///
|
||||||
|
/// ```toml
|
||||||
|
/// allow-renamed-params-for = [ "std::convert::From" ]
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// #### Noteworthy
|
||||||
|
///
|
||||||
|
/// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr`
|
||||||
|
/// - `".."` can be used as part of the list to indicate that the configured values should be appended to the
|
||||||
|
/// default configuration of Clippy. By default, any configuration will replace the default value.
|
||||||
|
(allow_renamed_params_for: Vec<String> =
|
||||||
|
DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()),
|
||||||
|
/// Lint: MACRO_METAVARS_IN_UNSAFE.
|
||||||
|
///
|
||||||
|
/// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros.
|
||||||
|
(warn_unsafe_macro_metavars_in_private_macros: bool = false),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for the configuration file.
|
/// Search for the configuration file.
|
||||||
|
@ -674,6 +697,10 @@ fn deserialize(file: &SourceFile) -> TryConf {
|
||||||
extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
|
extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
|
||||||
extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
|
extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
|
||||||
extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES);
|
extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES);
|
||||||
|
extend_vec_if_indicator_present(
|
||||||
|
&mut conf.conf.allow_renamed_params_for,
|
||||||
|
DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS,
|
||||||
|
);
|
||||||
// TODO: THIS SHOULD BE TESTED, this comment will be gone soon
|
// TODO: THIS SHOULD BE TESTED, this comment will be gone soon
|
||||||
if conf.conf.allowed_idents_below_min_chars.contains("..") {
|
if conf.conf.allowed_idents_below_min_chars.contains("..") {
|
||||||
conf.conf
|
conf.conf
|
||||||
|
|
|
@ -26,7 +26,8 @@ msrv_aliases! {
|
||||||
1,63,0 { CLONE_INTO }
|
1,63,0 { CLONE_INTO }
|
||||||
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
|
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
|
||||||
1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST }
|
1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST }
|
||||||
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }
|
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF }
|
||||||
|
1,56,0 { CONST_FN_UNION }
|
||||||
1,55,0 { SEEK_REWIND }
|
1,55,0 { SEEK_REWIND }
|
||||||
1,54,0 { INTO_KEYS }
|
1,54,0 { INTO_KEYS }
|
||||||
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
|
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_dev"
|
name = "clippy_dev"
|
||||||
|
description = "Clippy developer tooling"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aho-corasick = "1.0"
|
aho-corasick = "1.0"
|
||||||
clap = "4.1.4"
|
clap = { version = "4.4", features = ["derive"] }
|
||||||
indoc = "1.0"
|
indoc = "1.0"
|
||||||
itertools = "0.12"
|
itertools = "0.12"
|
||||||
opener = "0.6"
|
opener = "0.6"
|
||||||
|
|
|
@ -2,350 +2,292 @@
|
||||||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||||
|
|
||||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
use clap::{Args, Parser, Subcommand};
|
||||||
use clippy_dev::{dogfood, fmt, lint, new_lint, serve, setup, update_lints};
|
use clippy_dev::{dogfood, fmt, lint, new_lint, serve, setup, update_lints};
|
||||||
use indoc::indoc;
|
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = get_clap_config();
|
let dev = Dev::parse();
|
||||||
|
|
||||||
match matches.subcommand() {
|
match dev.command {
|
||||||
Some(("bless", _)) => {
|
DevCommand::Bless => {
|
||||||
eprintln!("use `cargo bless` to automatically replace `.stderr` and `.fixed` files as tests are being run");
|
eprintln!("use `cargo bless` to automatically replace `.stderr` and `.fixed` files as tests are being run");
|
||||||
},
|
},
|
||||||
Some(("dogfood", matches)) => {
|
DevCommand::Dogfood {
|
||||||
dogfood::dogfood(
|
fix,
|
||||||
matches.get_flag("fix"),
|
allow_dirty,
|
||||||
matches.get_flag("allow-dirty"),
|
allow_staged,
|
||||||
matches.get_flag("allow-staged"),
|
} => dogfood::dogfood(fix, allow_dirty, allow_staged),
|
||||||
);
|
DevCommand::Fmt { check, verbose } => fmt::run(check, verbose),
|
||||||
},
|
DevCommand::UpdateLints { print_only, check } => {
|
||||||
Some(("fmt", matches)) => {
|
if print_only {
|
||||||
fmt::run(matches.get_flag("check"), matches.get_flag("verbose"));
|
|
||||||
},
|
|
||||||
Some(("update_lints", matches)) => {
|
|
||||||
if matches.get_flag("print-only") {
|
|
||||||
update_lints::print_lints();
|
update_lints::print_lints();
|
||||||
} else if matches.get_flag("check") {
|
} else if check {
|
||||||
update_lints::update(update_lints::UpdateMode::Check);
|
update_lints::update(update_lints::UpdateMode::Check);
|
||||||
} else {
|
} else {
|
||||||
update_lints::update(update_lints::UpdateMode::Change);
|
update_lints::update(update_lints::UpdateMode::Change);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(("new_lint", matches)) => {
|
DevCommand::NewLint {
|
||||||
match new_lint::create(
|
pass,
|
||||||
matches.get_one::<String>("pass").unwrap(),
|
name,
|
||||||
matches.get_one::<String>("name"),
|
category,
|
||||||
matches.get_one::<String>("category").map(String::as_str),
|
r#type,
|
||||||
matches.get_one::<String>("type").map(String::as_str),
|
msrv,
|
||||||
matches.get_flag("msrv"),
|
} => match new_lint::create(&pass, &name, &category, r#type.as_deref(), msrv) {
|
||||||
) {
|
Ok(()) => update_lints::update(update_lints::UpdateMode::Change),
|
||||||
Ok(()) => update_lints::update(update_lints::UpdateMode::Change),
|
Err(e) => eprintln!("Unable to create lint: {e}"),
|
||||||
Err(e) => eprintln!("Unable to create lint: {e}"),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Some(("setup", sub_command)) => match sub_command.subcommand() {
|
DevCommand::Setup(SetupCommand { subcommand }) => match subcommand {
|
||||||
Some(("git-hook", matches)) => {
|
SetupSubcommand::Intellij { remove, repo_path } => {
|
||||||
if matches.get_flag("remove") {
|
if remove {
|
||||||
setup::git_hook::remove_hook();
|
|
||||||
} else {
|
|
||||||
setup::git_hook::install_hook(matches.get_flag("force-override"));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(("intellij", matches)) => {
|
|
||||||
if matches.get_flag("remove") {
|
|
||||||
setup::intellij::remove_rustc_src();
|
setup::intellij::remove_rustc_src();
|
||||||
} else {
|
} else {
|
||||||
setup::intellij::setup_rustc_src(
|
setup::intellij::setup_rustc_src(&repo_path);
|
||||||
matches
|
|
||||||
.get_one::<String>("rustc-repo-path")
|
|
||||||
.expect("this field is mandatory and therefore always valid"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(("toolchain", matches)) => {
|
SetupSubcommand::GitHook { remove, force_override } => {
|
||||||
setup::toolchain::create(
|
if remove {
|
||||||
matches.get_flag("force"),
|
setup::git_hook::remove_hook();
|
||||||
matches.get_flag("release"),
|
} else {
|
||||||
matches.get_one::<String>("name").unwrap(),
|
setup::git_hook::install_hook(force_override);
|
||||||
);
|
}
|
||||||
},
|
},
|
||||||
Some(("vscode-tasks", matches)) => {
|
SetupSubcommand::Toolchain { force, release, name } => setup::toolchain::create(force, release, &name),
|
||||||
if matches.get_flag("remove") {
|
SetupSubcommand::VscodeTasks { remove, force_override } => {
|
||||||
|
if remove {
|
||||||
setup::vscode::remove_tasks();
|
setup::vscode::remove_tasks();
|
||||||
} else {
|
} else {
|
||||||
setup::vscode::install_tasks(matches.get_flag("force-override"));
|
setup::vscode::install_tasks(force_override);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
|
||||||
},
|
},
|
||||||
Some(("remove", sub_command)) => match sub_command.subcommand() {
|
DevCommand::Remove(RemoveCommand { subcommand }) => match subcommand {
|
||||||
Some(("git-hook", _)) => setup::git_hook::remove_hook(),
|
RemoveSubcommand::Intellij => setup::intellij::remove_rustc_src(),
|
||||||
Some(("intellij", _)) => setup::intellij::remove_rustc_src(),
|
RemoveSubcommand::GitHook => setup::git_hook::remove_hook(),
|
||||||
Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(),
|
RemoveSubcommand::VscodeTasks => setup::vscode::remove_tasks(),
|
||||||
_ => {},
|
|
||||||
},
|
},
|
||||||
Some(("serve", matches)) => {
|
DevCommand::Serve { port, lint } => serve::run(port, lint),
|
||||||
let port = *matches.get_one::<u16>("port").unwrap();
|
DevCommand::Lint { path, args } => lint::run(&path, args.iter()),
|
||||||
let lint = matches.get_one::<String>("lint");
|
DevCommand::RenameLint {
|
||||||
serve::run(port, lint);
|
old_name,
|
||||||
},
|
new_name,
|
||||||
Some(("lint", matches)) => {
|
uplift,
|
||||||
let path = matches.get_one::<String>("path").unwrap();
|
} => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift),
|
||||||
let args = matches.get_many::<String>("args").into_iter().flatten();
|
DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, reason.as_deref()),
|
||||||
lint::run(path, args);
|
|
||||||
},
|
|
||||||
Some(("rename_lint", matches)) => {
|
|
||||||
let old_name = matches.get_one::<String>("old_name").unwrap();
|
|
||||||
let new_name = matches.get_one::<String>("new_name").unwrap_or(old_name);
|
|
||||||
let uplift = matches.get_flag("uplift");
|
|
||||||
update_lints::rename(old_name, new_name, uplift);
|
|
||||||
},
|
|
||||||
Some(("deprecate", matches)) => {
|
|
||||||
let name = matches.get_one::<String>("name").unwrap();
|
|
||||||
let reason = matches.get_one("reason");
|
|
||||||
update_lints::deprecate(name, reason);
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_clap_config() -> ArgMatches {
|
#[derive(Parser)]
|
||||||
Command::new("Clippy developer tooling")
|
#[command(name = "dev", about)]
|
||||||
.arg_required_else_help(true)
|
struct Dev {
|
||||||
.subcommands([
|
#[command(subcommand)]
|
||||||
Command::new("bless").about("bless the test output changes").arg(
|
command: DevCommand,
|
||||||
Arg::new("ignore-timestamp")
|
}
|
||||||
.long("ignore-timestamp")
|
|
||||||
.action(ArgAction::SetTrue)
|
#[derive(Subcommand)]
|
||||||
.help("Include files updated before clippy was built"),
|
enum DevCommand {
|
||||||
),
|
/// Bless the test output changes
|
||||||
Command::new("dogfood").about("Runs the dogfood test").args([
|
Bless,
|
||||||
Arg::new("fix")
|
/// Runs the dogfood test
|
||||||
.long("fix")
|
Dogfood {
|
||||||
.action(ArgAction::SetTrue)
|
#[arg(long)]
|
||||||
.help("Apply the suggestions when possible"),
|
/// Apply the suggestions when possible
|
||||||
Arg::new("allow-dirty")
|
fix: bool,
|
||||||
.long("allow-dirty")
|
#[arg(long, requires = "fix")]
|
||||||
.action(ArgAction::SetTrue)
|
/// Fix code even if the working directory has changes
|
||||||
.help("Fix code even if the working directory has changes")
|
allow_dirty: bool,
|
||||||
.requires("fix"),
|
#[arg(long, requires = "fix")]
|
||||||
Arg::new("allow-staged")
|
/// Fix code even if the working directory has staged changes
|
||||||
.long("allow-staged")
|
allow_staged: bool,
|
||||||
.action(ArgAction::SetTrue)
|
},
|
||||||
.help("Fix code even if the working directory has staged changes")
|
/// Run rustfmt on all projects and tests
|
||||||
.requires("fix"),
|
Fmt {
|
||||||
]),
|
#[arg(long)]
|
||||||
Command::new("fmt")
|
/// Use the rustfmt --check option
|
||||||
.about("Run rustfmt on all projects and tests")
|
check: bool,
|
||||||
.args([
|
#[arg(short, long)]
|
||||||
Arg::new("check")
|
/// Echo commands run
|
||||||
.long("check")
|
verbose: bool,
|
||||||
.action(ArgAction::SetTrue)
|
},
|
||||||
.help("Use the rustfmt --check option"),
|
#[command(name = "update_lints")]
|
||||||
Arg::new("verbose")
|
/// Updates lint registration and information from the source code
|
||||||
.short('v')
|
///
|
||||||
.long("verbose")
|
/// Makes sure that: {n}
|
||||||
.action(ArgAction::SetTrue)
|
/// * the lint count in README.md is correct {n}
|
||||||
.help("Echo commands run"),
|
/// * the changelog contains markdown link references at the bottom {n}
|
||||||
]),
|
/// * all lint groups include the correct lints {n}
|
||||||
Command::new("update_lints")
|
/// * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod` {n}
|
||||||
.about("Updates lint registration and information from the source code")
|
/// * all lints are registered in the lint store
|
||||||
.long_about(
|
UpdateLints {
|
||||||
"Makes sure that:\n \
|
#[arg(long)]
|
||||||
* the lint count in README.md is correct\n \
|
/// Print a table of lints to STDOUT
|
||||||
* the changelog contains markdown link references at the bottom\n \
|
///
|
||||||
* all lint groups include the correct lints\n \
|
/// This does not include deprecated and internal lints. (Does not modify any files)
|
||||||
* lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
|
print_only: bool,
|
||||||
* all lints are registered in the lint store",
|
#[arg(long)]
|
||||||
)
|
/// Checks that `cargo dev update_lints` has been run. Used on CI.
|
||||||
.args([
|
check: bool,
|
||||||
Arg::new("print-only")
|
},
|
||||||
.long("print-only")
|
#[command(name = "new_lint")]
|
||||||
.action(ArgAction::SetTrue)
|
/// Create a new lint and run `cargo dev update_lints`
|
||||||
.help(
|
NewLint {
|
||||||
"Print a table of lints to STDOUT. \
|
#[arg(short, long, value_parser = ["early", "late"], conflicts_with = "type", default_value = "late")]
|
||||||
This does not include deprecated and internal lints. \
|
/// Specify whether the lint runs during the early or late pass
|
||||||
(Does not modify any files)",
|
pass: String,
|
||||||
),
|
#[arg(
|
||||||
Arg::new("check")
|
short,
|
||||||
.long("check")
|
long,
|
||||||
.action(ArgAction::SetTrue)
|
value_parser = |name: &str| Ok::<_, Infallible>(name.replace('-', "_")),
|
||||||
.help("Checks that `cargo dev update_lints` has been run. Used on CI."),
|
)]
|
||||||
]),
|
/// Name of the new lint in snake case, ex: `fn_too_long`
|
||||||
Command::new("new_lint")
|
name: String,
|
||||||
.about("Create new lint and run `cargo dev update_lints`")
|
#[arg(
|
||||||
.args([
|
short,
|
||||||
Arg::new("pass")
|
long,
|
||||||
.short('p')
|
value_parser = [
|
||||||
.long("pass")
|
"style",
|
||||||
.help("Specify whether the lint runs during the early or late pass")
|
"correctness",
|
||||||
.value_parser(["early", "late"])
|
"suspicious",
|
||||||
.conflicts_with("type")
|
"complexity",
|
||||||
.default_value("late"),
|
"perf",
|
||||||
Arg::new("name")
|
"pedantic",
|
||||||
.short('n')
|
"restriction",
|
||||||
.long("name")
|
"cargo",
|
||||||
.help("Name of the new lint in snake case, ex: fn_too_long")
|
"nursery",
|
||||||
.required(true)
|
"internal",
|
||||||
.value_parser(|name: &str| Ok::<_, Infallible>(name.replace('-', "_"))),
|
],
|
||||||
Arg::new("category")
|
default_value = "nursery",
|
||||||
.short('c')
|
)]
|
||||||
.long("category")
|
/// What category the lint belongs to
|
||||||
.help("What category the lint belongs to")
|
category: String,
|
||||||
.default_value("nursery")
|
#[arg(long)]
|
||||||
.value_parser([
|
/// What directory the lint belongs in
|
||||||
"style",
|
r#type: Option<String>,
|
||||||
"correctness",
|
#[arg(long)]
|
||||||
"suspicious",
|
/// Add MSRV config code to the lint
|
||||||
"complexity",
|
msrv: bool,
|
||||||
"perf",
|
},
|
||||||
"pedantic",
|
/// Support for setting up your personal development environment
|
||||||
"restriction",
|
Setup(SetupCommand),
|
||||||
"cargo",
|
/// Support for removing changes done by the setup command
|
||||||
"nursery",
|
Remove(RemoveCommand),
|
||||||
"internal",
|
/// Launch a local 'ALL the Clippy Lints' website in a browser
|
||||||
]),
|
Serve {
|
||||||
Arg::new("type").long("type").help("What directory the lint belongs in"),
|
#[arg(short, long, default_value = "8000")]
|
||||||
Arg::new("msrv")
|
/// Local port for the http server
|
||||||
.long("msrv")
|
port: u16,
|
||||||
.action(ArgAction::SetTrue)
|
#[arg(long)]
|
||||||
.help("Add MSRV config code to the lint"),
|
/// Which lint's page to load initially (optional)
|
||||||
]),
|
lint: Option<String>,
|
||||||
Command::new("setup")
|
},
|
||||||
.about("Support for setting up your personal development environment")
|
#[allow(clippy::doc_markdown)]
|
||||||
.arg_required_else_help(true)
|
/// Manually run clippy on a file or package
|
||||||
.subcommands([
|
///
|
||||||
Command::new("git-hook")
|
/// ## Examples
|
||||||
.about("Add a pre-commit git hook that formats your code to make it look pretty")
|
///
|
||||||
.args([
|
/// Lint a single file: {n}
|
||||||
Arg::new("remove")
|
/// cargo dev lint tests/ui/attrs.rs
|
||||||
.long("remove")
|
///
|
||||||
.action(ArgAction::SetTrue)
|
/// Lint a package directory: {n}
|
||||||
.help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"),
|
/// cargo dev lint tests/ui-cargo/wildcard_dependencies/fail {n}
|
||||||
Arg::new("force-override")
|
/// cargo dev lint ~/my-project
|
||||||
.long("force-override")
|
///
|
||||||
.short('f')
|
/// Run rustfix: {n}
|
||||||
.action(ArgAction::SetTrue)
|
/// cargo dev lint ~/my-project -- --fix
|
||||||
.help("Forces the override of an existing git pre-commit hook"),
|
///
|
||||||
]),
|
/// Set lint levels: {n}
|
||||||
Command::new("intellij")
|
/// cargo dev lint file.rs -- -W clippy::pedantic {n}
|
||||||
.about("Alter dependencies so Intellij Rust can find rustc internals")
|
/// cargo dev lint ~/my-project -- -- -W clippy::pedantic
|
||||||
.args([
|
Lint {
|
||||||
Arg::new("remove")
|
/// The path to a file or package directory to lint
|
||||||
.long("remove")
|
path: String,
|
||||||
.action(ArgAction::SetTrue)
|
/// Pass extra arguments to cargo/clippy-driver
|
||||||
.help("Remove the dependencies added with 'cargo dev setup intellij'"),
|
args: Vec<String>,
|
||||||
Arg::new("rustc-repo-path")
|
},
|
||||||
.long("repo-path")
|
#[command(name = "rename_lint")]
|
||||||
.short('r')
|
/// Rename a lint
|
||||||
.help("The path to a rustc repo that will be used for setting the dependencies")
|
RenameLint {
|
||||||
.value_name("path")
|
/// The name of the lint to rename
|
||||||
.conflicts_with("remove")
|
old_name: String,
|
||||||
.required(true),
|
#[arg(required_unless_present = "uplift")]
|
||||||
]),
|
/// The new name of the lint
|
||||||
Command::new("toolchain")
|
new_name: Option<String>,
|
||||||
.about("Install a rustup toolchain pointing to the local clippy build")
|
#[arg(long)]
|
||||||
.args([
|
/// This lint will be uplifted into rustc
|
||||||
Arg::new("force")
|
uplift: bool,
|
||||||
.long("force")
|
},
|
||||||
.short('f')
|
/// Deprecate the given lint
|
||||||
.action(ArgAction::SetTrue)
|
Deprecate {
|
||||||
.help("Override an existing toolchain"),
|
/// The name of the lint to deprecate
|
||||||
Arg::new("release")
|
name: String,
|
||||||
.long("release")
|
#[arg(long, short)]
|
||||||
.short('r')
|
/// The reason for deprecation
|
||||||
.action(ArgAction::SetTrue)
|
reason: Option<String>,
|
||||||
.help("Point to --release clippy binaries"),
|
},
|
||||||
Arg::new("name")
|
}
|
||||||
.long("name")
|
|
||||||
.default_value("clippy")
|
#[derive(Args)]
|
||||||
.help("The name of the created toolchain"),
|
struct SetupCommand {
|
||||||
]),
|
#[command(subcommand)]
|
||||||
Command::new("vscode-tasks")
|
subcommand: SetupSubcommand,
|
||||||
.about("Add several tasks to vscode for formatting, validation and testing")
|
}
|
||||||
.args([
|
|
||||||
Arg::new("remove")
|
#[derive(Subcommand)]
|
||||||
.long("remove")
|
enum SetupSubcommand {
|
||||||
.action(ArgAction::SetTrue)
|
/// Alter dependencies so Intellij Rust can find rustc internals
|
||||||
.help("Remove the tasks added with 'cargo dev setup vscode-tasks'"),
|
Intellij {
|
||||||
Arg::new("force-override")
|
#[arg(long)]
|
||||||
.long("force-override")
|
/// Remove the dependencies added with 'cargo dev setup intellij'
|
||||||
.short('f')
|
remove: bool,
|
||||||
.action(ArgAction::SetTrue)
|
#[arg(long, short, conflicts_with = "remove")]
|
||||||
.help("Forces the override of existing vscode tasks"),
|
/// The path to a rustc repo that will be used for setting the dependencies
|
||||||
]),
|
repo_path: String,
|
||||||
]),
|
},
|
||||||
Command::new("remove")
|
/// Add a pre-commit git hook that formats your code to make it look pretty
|
||||||
.about("Support for undoing changes done by the setup command")
|
GitHook {
|
||||||
.arg_required_else_help(true)
|
#[arg(long)]
|
||||||
.subcommands([
|
/// Remove the pre-commit hook added with 'cargo dev setup git-hook'
|
||||||
Command::new("git-hook").about("Remove any existing pre-commit git hook"),
|
remove: bool,
|
||||||
Command::new("vscode-tasks").about("Remove any existing vscode tasks"),
|
#[arg(long, short)]
|
||||||
Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"),
|
/// Forces the override of an existing git pre-commit hook
|
||||||
]),
|
force_override: bool,
|
||||||
Command::new("serve")
|
},
|
||||||
.about("Launch a local 'ALL the Clippy Lints' website in a browser")
|
/// Install a rustup toolchain pointing to the local clippy build
|
||||||
.args([
|
Toolchain {
|
||||||
Arg::new("port")
|
#[arg(long, short)]
|
||||||
.long("port")
|
/// Override an existing toolchain
|
||||||
.short('p')
|
force: bool,
|
||||||
.help("Local port for the http server")
|
#[arg(long, short)]
|
||||||
.default_value("8000")
|
/// Point to --release clippy binary
|
||||||
.value_parser(clap::value_parser!(u16)),
|
release: bool,
|
||||||
Arg::new("lint").help("Which lint's page to load initially (optional)"),
|
#[arg(long, default_value = "clippy")]
|
||||||
]),
|
/// Name of the toolchain
|
||||||
Command::new("lint")
|
name: String,
|
||||||
.about("Manually run clippy on a file or package")
|
},
|
||||||
.after_help(indoc! {"
|
/// Add several tasks to vscode for formatting, validation and testing
|
||||||
EXAMPLES
|
VscodeTasks {
|
||||||
Lint a single file:
|
#[arg(long)]
|
||||||
cargo dev lint tests/ui/attrs.rs
|
/// Remove the tasks added with 'cargo dev setup vscode-tasks'
|
||||||
|
remove: bool,
|
||||||
Lint a package directory:
|
#[arg(long, short)]
|
||||||
cargo dev lint tests/ui-cargo/wildcard_dependencies/fail
|
/// Forces the override of existing vscode tasks
|
||||||
cargo dev lint ~/my-project
|
force_override: bool,
|
||||||
|
},
|
||||||
Run rustfix:
|
}
|
||||||
cargo dev lint ~/my-project -- --fix
|
|
||||||
|
#[derive(Args)]
|
||||||
Set lint levels:
|
struct RemoveCommand {
|
||||||
cargo dev lint file.rs -- -W clippy::pedantic
|
#[command(subcommand)]
|
||||||
cargo dev lint ~/my-project -- -- -W clippy::pedantic
|
subcommand: RemoveSubcommand,
|
||||||
"})
|
}
|
||||||
.args([
|
|
||||||
Arg::new("path")
|
#[derive(Subcommand)]
|
||||||
.required(true)
|
enum RemoveSubcommand {
|
||||||
.help("The path to a file or package directory to lint"),
|
/// Remove the dependencies added with 'cargo dev setup intellij'
|
||||||
Arg::new("args")
|
Intellij,
|
||||||
.action(ArgAction::Append)
|
/// Remove the pre-commit git hook
|
||||||
.help("Pass extra arguments to cargo/clippy-driver"),
|
GitHook,
|
||||||
]),
|
/// Remove the tasks added with 'cargo dev setup vscode-tasks'
|
||||||
Command::new("rename_lint").about("Renames the given lint").args([
|
VscodeTasks,
|
||||||
Arg::new("old_name")
|
|
||||||
.index(1)
|
|
||||||
.required(true)
|
|
||||||
.help("The name of the lint to rename"),
|
|
||||||
Arg::new("new_name")
|
|
||||||
.index(2)
|
|
||||||
.required_unless_present("uplift")
|
|
||||||
.help("The new name of the lint"),
|
|
||||||
Arg::new("uplift")
|
|
||||||
.long("uplift")
|
|
||||||
.action(ArgAction::SetTrue)
|
|
||||||
.help("This lint will be uplifted into rustc"),
|
|
||||||
]),
|
|
||||||
Command::new("deprecate").about("Deprecates the given lint").args([
|
|
||||||
Arg::new("name")
|
|
||||||
.index(1)
|
|
||||||
.required(true)
|
|
||||||
.help("The name of the lint to deprecate"),
|
|
||||||
Arg::new("reason")
|
|
||||||
.long("reason")
|
|
||||||
.short('r')
|
|
||||||
.help("The reason for deprecation"),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
.get_matches()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,22 +36,16 @@ impl<T> Context for io::Result<T> {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// This function errors out if the files couldn't be created or written to.
|
/// This function errors out if the files couldn't be created or written to.
|
||||||
pub fn create(
|
pub fn create(pass: &str, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> {
|
||||||
pass: &String,
|
if category == "cargo" && ty.is_none() {
|
||||||
lint_name: Option<&String>,
|
|
||||||
category: Option<&str>,
|
|
||||||
mut ty: Option<&str>,
|
|
||||||
msrv: bool,
|
|
||||||
) -> io::Result<()> {
|
|
||||||
if category == Some("cargo") && ty.is_none() {
|
|
||||||
// `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo`
|
// `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo`
|
||||||
ty = Some("cargo");
|
ty = Some("cargo");
|
||||||
}
|
}
|
||||||
|
|
||||||
let lint = LintData {
|
let lint = LintData {
|
||||||
pass,
|
pass,
|
||||||
name: lint_name.expect("`name` argument is validated by clap"),
|
name,
|
||||||
category: category.expect("`category` argument is validated by clap"),
|
category,
|
||||||
ty,
|
ty,
|
||||||
project_root: clippy_project_root(),
|
project_root: clippy_project_root(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::{env, thread};
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the python commands could not be spawned
|
/// Panics if the python commands could not be spawned
|
||||||
pub fn run(port: u16, lint: Option<&String>) -> ! {
|
pub fn run(port: u16, lint: Option<String>) -> ! {
|
||||||
let mut url = Some(match lint {
|
let mut url = Some(match lint {
|
||||||
None => format!("http://localhost:{port}"),
|
None => format!("http://localhost:{port}"),
|
||||||
Some(lint) => format!("http://localhost:{port}/#{lint}"),
|
Some(lint) => format!("http://localhost:{port}/#{lint}"),
|
||||||
|
|
|
@ -314,7 +314,7 @@ const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If a file path could not read from or written to
|
/// If a file path could not read from or written to
|
||||||
pub fn deprecate(name: &str, reason: Option<&String>) {
|
pub fn deprecate(name: &str, reason: Option<&str>) {
|
||||||
fn finish(
|
fn finish(
|
||||||
(lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
|
(lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
|
||||||
name: &str,
|
name: &str,
|
||||||
|
@ -335,7 +335,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) {
|
||||||
println!("note: you must run `cargo uitest` to update the test results");
|
println!("note: you must run `cargo uitest` to update the test results");
|
||||||
}
|
}
|
||||||
|
|
||||||
let reason = reason.map_or(DEFAULT_DEPRECATION_REASON, String::as_str);
|
let reason = reason.unwrap_or(DEFAULT_DEPRECATION_REASON);
|
||||||
let name_lower = name.to_lowercase();
|
let name_lower = name.to_lowercase();
|
||||||
let name_upper = name.to_uppercase();
|
let name_upper = name.to_uppercase();
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,15 @@ use clippy_config::msrvs::{self, Msrv};
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::macros::HirNode;
|
use clippy_utils::macros::HirNode;
|
||||||
use clippy_utils::sugg::Sugg;
|
use clippy_utils::sugg::Sugg;
|
||||||
use clippy_utils::{is_trait_method, path_to_local};
|
use clippy_utils::{is_trait_method, local_is_initialized, path_to_local};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{self as hir, Expr, ExprKind, Node};
|
use rustc_hir::{self as hir, Expr, ExprKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{self, Instance, Mutability};
|
use rustc_middle::ty::{self, Instance, Mutability};
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::ExpnKind;
|
use rustc_span::{ExpnKind, SyntaxContext};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
@ -36,6 +36,7 @@ declare_clippy_lint! {
|
||||||
/// Use instead:
|
/// Use instead:
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// struct Thing;
|
/// struct Thing;
|
||||||
|
///
|
||||||
/// impl Clone for Thing {
|
/// impl Clone for Thing {
|
||||||
/// fn clone(&self) -> Self { todo!() }
|
/// fn clone(&self) -> Self { todo!() }
|
||||||
/// fn clone_from(&mut self, other: &Self) { todo!() }
|
/// fn clone_from(&mut self, other: &Self) { todo!() }
|
||||||
|
@ -47,7 +48,7 @@ declare_clippy_lint! {
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.78.0"]
|
#[clippy::version = "1.78.0"]
|
||||||
pub ASSIGNING_CLONES,
|
pub ASSIGNING_CLONES,
|
||||||
perf,
|
pedantic,
|
||||||
"assigning the result of cloning may be inefficient"
|
"assigning the result of cloning may be inefficient"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +68,8 @@ impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]);
|
||||||
impl<'tcx> LateLintPass<'tcx> for AssigningClones {
|
impl<'tcx> LateLintPass<'tcx> for AssigningClones {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx Expr<'_>) {
|
||||||
// Do not fire the lint in macros
|
// Do not fire the lint in macros
|
||||||
let expn_data = assign_expr.span().ctxt().outer_expn_data();
|
let ctxt = assign_expr.span().ctxt();
|
||||||
|
let expn_data = ctxt.outer_expn_data();
|
||||||
match expn_data.kind {
|
match expn_data.kind {
|
||||||
ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) | ExpnKind::Macro(..) => return,
|
ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) | ExpnKind::Macro(..) => return,
|
||||||
ExpnKind::Root => {},
|
ExpnKind::Root => {},
|
||||||
|
@ -82,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_ok_to_suggest(cx, lhs, &call, &self.msrv) {
|
if is_ok_to_suggest(cx, lhs, &call, &self.msrv) {
|
||||||
suggest(cx, assign_expr, lhs, &call);
|
suggest(cx, ctxt, assign_expr, lhs, &call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,9 +165,7 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC
|
||||||
// TODO: This check currently bails if the local variable has no initializer.
|
// TODO: This check currently bails if the local variable has no initializer.
|
||||||
// That is overly conservative - the lint should fire even if there was no initializer,
|
// That is overly conservative - the lint should fire even if there was no initializer,
|
||||||
// but the variable has been initialized before `lhs` was evaluated.
|
// but the variable has been initialized before `lhs` was evaluated.
|
||||||
if let Some(Node::LetStmt(local)) = cx.tcx.hir().parent_id_iter(local).next().map(|p| cx.tcx.hir_node(p))
|
if !local_is_initialized(cx, local) {
|
||||||
&& local.init.is_none()
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,14 +222,20 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC
|
||||||
implemented_fns.contains_key(&provided_fn.def_id)
|
implemented_fns.contains_key(&provided_fn.def_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suggest<'tcx>(cx: &LateContext<'tcx>, assign_expr: &Expr<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>) {
|
fn suggest<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
ctxt: SyntaxContext,
|
||||||
|
assign_expr: &Expr<'tcx>,
|
||||||
|
lhs: &Expr<'tcx>,
|
||||||
|
call: &CallCandidate<'tcx>,
|
||||||
|
) {
|
||||||
span_lint_and_then(cx, ASSIGNING_CLONES, assign_expr.span, call.message(), |diag| {
|
span_lint_and_then(cx, ASSIGNING_CLONES, assign_expr.span, call.message(), |diag| {
|
||||||
let mut applicability = Applicability::Unspecified;
|
let mut applicability = Applicability::Unspecified;
|
||||||
|
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
assign_expr.span,
|
assign_expr.span,
|
||||||
call.suggestion_msg(),
|
call.suggestion_msg(),
|
||||||
call.suggested_replacement(cx, lhs, &mut applicability),
|
call.suggested_replacement(cx, ctxt, lhs, &mut applicability),
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -275,6 +281,7 @@ impl<'tcx> CallCandidate<'tcx> {
|
||||||
fn suggested_replacement(
|
fn suggested_replacement(
|
||||||
&self,
|
&self,
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
|
ctxt: SyntaxContext,
|
||||||
lhs: &Expr<'tcx>,
|
lhs: &Expr<'tcx>,
|
||||||
applicability: &mut Applicability,
|
applicability: &mut Applicability,
|
||||||
) -> String {
|
) -> String {
|
||||||
|
@ -294,7 +301,7 @@ impl<'tcx> CallCandidate<'tcx> {
|
||||||
// Determine whether we need to reference the argument to clone_from().
|
// Determine whether we need to reference the argument to clone_from().
|
||||||
let clone_receiver_type = cx.typeck_results().expr_ty(receiver);
|
let clone_receiver_type = cx.typeck_results().expr_ty(receiver);
|
||||||
let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(receiver);
|
let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(receiver);
|
||||||
let mut arg_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability);
|
let mut arg_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability);
|
||||||
if clone_receiver_type != clone_receiver_adj_type {
|
if clone_receiver_type != clone_receiver_adj_type {
|
||||||
// The receiver may have been a value type, so we need to add an `&` to
|
// The receiver may have been a value type, so we need to add an `&` to
|
||||||
// be sure the argument to clone_from will be a reference.
|
// be sure the argument to clone_from will be a reference.
|
||||||
|
@ -312,7 +319,7 @@ impl<'tcx> CallCandidate<'tcx> {
|
||||||
Sugg::hir_with_applicability(cx, lhs, "_", applicability).mut_addr()
|
Sugg::hir_with_applicability(cx, lhs, "_", applicability).mut_addr()
|
||||||
};
|
};
|
||||||
// The RHS had to be exactly correct before the call, there is no auto-deref for function calls.
|
// The RHS had to be exactly correct before the call, there is no auto-deref for function calls.
|
||||||
let rhs_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability);
|
let rhs_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability);
|
||||||
|
|
||||||
format!("Clone::clone_from({self_sugg}, {rhs_sugg})")
|
format!("Clone::clone_from({self_sugg}, {rhs_sugg})")
|
||||||
},
|
},
|
||||||
|
@ -341,11 +348,11 @@ impl<'tcx> CallCandidate<'tcx> {
|
||||||
|
|
||||||
match self.kind {
|
match self.kind {
|
||||||
CallKind::MethodCall { receiver } => {
|
CallKind::MethodCall { receiver } => {
|
||||||
let receiver_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability);
|
let receiver_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability);
|
||||||
format!("{receiver_sugg}.clone_into({rhs_sugg})")
|
format!("{receiver_sugg}.clone_into({rhs_sugg})")
|
||||||
},
|
},
|
||||||
CallKind::FunctionCall { self_arg, .. } => {
|
CallKind::FunctionCall { self_arg, .. } => {
|
||||||
let self_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability);
|
let self_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability);
|
||||||
format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})")
|
format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,10 @@ fn check_duplicated_attr(
|
||||||
}
|
}
|
||||||
let Some(ident) = attr.ident() else { return };
|
let Some(ident) = attr.ident() else { return };
|
||||||
let name = ident.name;
|
let name = ident.name;
|
||||||
if name == sym::doc || name == sym::cfg_attr {
|
if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented {
|
||||||
// FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
|
// FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
|
||||||
// conditions are the same.
|
// conditions are the same.
|
||||||
|
// `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(direct_parent) = parent.last()
|
if let Some(direct_parent) = parent.last()
|
||||||
|
|
|
@ -61,11 +61,21 @@ declare_clippy_lint! {
|
||||||
///
|
///
|
||||||
/// This lint permits lint attributes for lints emitted on the items themself.
|
/// This lint permits lint attributes for lints emitted on the items themself.
|
||||||
/// For `use` items these lints are:
|
/// For `use` items these lints are:
|
||||||
|
/// * ambiguous_glob_reexports
|
||||||
|
/// * dead_code
|
||||||
/// * deprecated
|
/// * deprecated
|
||||||
|
/// * hidden_glob_reexports
|
||||||
/// * unreachable_pub
|
/// * unreachable_pub
|
||||||
/// * unused_imports
|
/// * unused
|
||||||
|
/// * unused_braces
|
||||||
|
/// * unused_import_braces
|
||||||
|
/// * clippy::disallowed_types
|
||||||
/// * clippy::enum_glob_use
|
/// * clippy::enum_glob_use
|
||||||
/// * clippy::macro_use_imports
|
/// * clippy::macro_use_imports
|
||||||
|
/// * clippy::module_name_repetitions
|
||||||
|
/// * clippy::redundant_pub_crate
|
||||||
|
/// * clippy::single_component_path_imports
|
||||||
|
/// * clippy::unsafe_removed_from_name
|
||||||
/// * clippy::wildcard_imports
|
/// * clippy::wildcard_imports
|
||||||
///
|
///
|
||||||
/// For `extern crate` items these lints are:
|
/// For `extern crate` items these lints are:
|
||||||
|
|
|
@ -2,6 +2,7 @@ use super::utils::{extract_clippy_lint, is_lint_level, is_word};
|
||||||
use super::{Attribute, USELESS_ATTRIBUTE};
|
use super::{Attribute, USELESS_ATTRIBUTE};
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::source::{first_line_of_span, snippet_opt};
|
use clippy_utils::source::{first_line_of_span, snippet_opt};
|
||||||
|
use rustc_ast::NestedMetaItem;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Item, ItemKind};
|
use rustc_hir::{Item, ItemKind};
|
||||||
use rustc_lint::{LateContext, LintContext};
|
use rustc_lint::{LateContext, LintContext};
|
||||||
|
@ -20,26 +21,40 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
|
||||||
for lint in lint_list {
|
for lint in lint_list {
|
||||||
match item.kind {
|
match item.kind {
|
||||||
ItemKind::Use(..) => {
|
ItemKind::Use(..) => {
|
||||||
if is_word(lint, sym::unused_imports)
|
if let NestedMetaItem::MetaItem(meta_item) = lint
|
||||||
|| is_word(lint, sym::deprecated)
|
&& meta_item.is_word()
|
||||||
|| is_word(lint, sym!(unreachable_pub))
|
&& let Some(ident) = meta_item.ident()
|
||||||
|| is_word(lint, sym!(unused))
|
&& matches!(
|
||||||
|| is_word(lint, sym!(unused_import_braces))
|
ident.name.as_str(),
|
||||||
|| extract_clippy_lint(lint).map_or(false, |s| {
|
"ambiguous_glob_reexports"
|
||||||
matches!(
|
| "dead_code"
|
||||||
s.as_str(),
|
| "deprecated"
|
||||||
"wildcard_imports"
|
| "hidden_glob_reexports"
|
||||||
| "enum_glob_use"
|
| "unreachable_pub"
|
||||||
| "redundant_pub_crate"
|
| "unused"
|
||||||
| "macro_use_imports"
|
| "unused_braces"
|
||||||
| "unsafe_removed_from_name"
|
| "unused_import_braces"
|
||||||
| "module_name_repetitions"
|
| "unused_imports"
|
||||||
| "single_component_path_imports"
|
)
|
||||||
)
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if extract_clippy_lint(lint).is_some_and(|symbol| {
|
||||||
|
matches!(
|
||||||
|
symbol.as_str(),
|
||||||
|
"wildcard_imports"
|
||||||
|
| "enum_glob_use"
|
||||||
|
| "redundant_pub_crate"
|
||||||
|
| "macro_use_imports"
|
||||||
|
| "unsafe_removed_from_name"
|
||||||
|
| "module_name_repetitions"
|
||||||
|
| "single_component_path_imports"
|
||||||
|
| "disallowed_types"
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ItemKind::ExternCrate(..) => {
|
ItemKind::ExternCrate(..) => {
|
||||||
if is_word(lint, sym::unused_imports) && skip_unused_imports {
|
if is_word(lint, sym::unused_imports) && skip_unused_imports {
|
||||||
|
|
|
@ -49,7 +49,7 @@ impl LintConfig {
|
||||||
|
|
||||||
type LintTable = BTreeMap<Spanned<String>, Spanned<LintConfig>>;
|
type LintTable = BTreeMap<Spanned<String>, Spanned<LintConfig>>;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug, Default)]
|
||||||
struct Lints {
|
struct Lints {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
rust: LintTable,
|
rust: LintTable,
|
||||||
|
@ -57,9 +57,18 @@ struct Lints {
|
||||||
clippy: LintTable,
|
clippy: LintTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Default)]
|
||||||
|
struct Workspace {
|
||||||
|
#[serde(default)]
|
||||||
|
lints: Lints,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct CargoToml {
|
struct CargoToml {
|
||||||
|
#[serde(default)]
|
||||||
lints: Lints,
|
lints: Lints,
|
||||||
|
#[serde(default)]
|
||||||
|
workspace: Workspace,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
@ -164,5 +173,7 @@ pub fn check(cx: &LateContext<'_>) {
|
||||||
|
|
||||||
check_table(cx, cargo_toml.lints.rust, &rustc_groups, &file);
|
check_table(cx, cargo_toml.lints.rust, &rustc_groups, &file);
|
||||||
check_table(cx, cargo_toml.lints.clippy, &clippy_groups, &file);
|
check_table(cx, cargo_toml.lints.clippy, &clippy_groups, &file);
|
||||||
|
check_table(cx, cargo_toml.workspace.lints.rust, &rustc_groups, &file);
|
||||||
|
check_table(cx, cargo_toml.workspace.lints.clippy, &clippy_groups, &file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,8 +255,10 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign {
|
||||||
|
|
||||||
/// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`],
|
/// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`],
|
||||||
/// where the result depends on:
|
/// where the result depends on:
|
||||||
|
///
|
||||||
/// - the number of negative values in the entire expression, or
|
/// - the number of negative values in the entire expression, or
|
||||||
/// - the number of negative values on the left hand side of the expression.
|
/// - the number of negative values on the left hand side of the expression.
|
||||||
|
///
|
||||||
/// Ignores overflow.
|
/// Ignores overflow.
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
|
@ -303,8 +305,10 @@ fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Peels binary operators such as [`BinOpKind::Add`], where the result depends on:
|
/// Peels binary operators such as [`BinOpKind::Add`], where the result depends on:
|
||||||
|
///
|
||||||
/// - all the expressions being positive, or
|
/// - all the expressions being positive, or
|
||||||
/// - all the expressions being negative.
|
/// - all the expressions being negative.
|
||||||
|
///
|
||||||
/// Ignores overflow.
|
/// Ignores overflow.
|
||||||
///
|
///
|
||||||
/// Expressions using other operators are preserved, so we can try to evaluate them later.
|
/// Expressions using other operators are preserved, so we can try to evaluate them later.
|
||||||
|
|
|
@ -140,6 +140,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::disallowed_names::DISALLOWED_NAMES_INFO,
|
crate::disallowed_names::DISALLOWED_NAMES_INFO,
|
||||||
crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO,
|
crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO,
|
||||||
crate::disallowed_types::DISALLOWED_TYPES_INFO,
|
crate::disallowed_types::DISALLOWED_TYPES_INFO,
|
||||||
|
crate::doc::DOC_LAZY_CONTINUATION_INFO,
|
||||||
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
|
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
|
||||||
crate::doc::DOC_MARKDOWN_INFO,
|
crate::doc::DOC_MARKDOWN_INFO,
|
||||||
crate::doc::EMPTY_DOCS_INFO,
|
crate::doc::EMPTY_DOCS_INFO,
|
||||||
|
@ -205,6 +206,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::functions::MUST_USE_CANDIDATE_INFO,
|
crate::functions::MUST_USE_CANDIDATE_INFO,
|
||||||
crate::functions::MUST_USE_UNIT_INFO,
|
crate::functions::MUST_USE_UNIT_INFO,
|
||||||
crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
|
crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
|
||||||
|
crate::functions::RENAMED_FUNCTION_PARAMS_INFO,
|
||||||
crate::functions::RESULT_LARGE_ERR_INFO,
|
crate::functions::RESULT_LARGE_ERR_INFO,
|
||||||
crate::functions::RESULT_UNIT_ERR_INFO,
|
crate::functions::RESULT_UNIT_ERR_INFO,
|
||||||
crate::functions::TOO_MANY_ARGUMENTS_INFO,
|
crate::functions::TOO_MANY_ARGUMENTS_INFO,
|
||||||
|
@ -294,6 +296,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::loops::WHILE_IMMUTABLE_CONDITION_INFO,
|
crate::loops::WHILE_IMMUTABLE_CONDITION_INFO,
|
||||||
crate::loops::WHILE_LET_LOOP_INFO,
|
crate::loops::WHILE_LET_LOOP_INFO,
|
||||||
crate::loops::WHILE_LET_ON_ITERATOR_INFO,
|
crate::loops::WHILE_LET_ON_ITERATOR_INFO,
|
||||||
|
crate::macro_metavars_in_unsafe::MACRO_METAVARS_IN_UNSAFE_INFO,
|
||||||
crate::macro_use::MACRO_USE_IMPORTS_INFO,
|
crate::macro_use::MACRO_USE_IMPORTS_INFO,
|
||||||
crate::main_recursion::MAIN_RECURSION_INFO,
|
crate::main_recursion::MAIN_RECURSION_INFO,
|
||||||
crate::manual_assert::MANUAL_ASSERT_INFO,
|
crate::manual_assert::MANUAL_ASSERT_INFO,
|
||||||
|
|
95
clippy_lints/src/doc/lazy_continuation.rs
Normal file
95
clippy_lints/src/doc/lazy_continuation.rs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use rustc_errors::{Applicability, SuggestionStyle};
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_span::{BytePos, Span};
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use super::DOC_LAZY_CONTINUATION;
|
||||||
|
|
||||||
|
fn map_container_to_text(c: &super::Container) -> &'static str {
|
||||||
|
match c {
|
||||||
|
super::Container::Blockquote => "> ",
|
||||||
|
// numbered list can have up to nine digits, plus the dot, plus four spaces on either side
|
||||||
|
super::Container::List(indent) => &" "[0..*indent],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Adjust the parameters as necessary
|
||||||
|
pub(super) fn check(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
doc: &str,
|
||||||
|
range: Range<usize>,
|
||||||
|
mut span: Span,
|
||||||
|
containers: &[super::Container],
|
||||||
|
) {
|
||||||
|
if doc[range.clone()].contains('\t') {
|
||||||
|
// We don't do tab stops correctly.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count();
|
||||||
|
let blockquote_level = containers
|
||||||
|
.iter()
|
||||||
|
.filter(|c| matches!(c, super::Container::Blockquote))
|
||||||
|
.count();
|
||||||
|
let lcount = doc[range.clone()].chars().filter(|c| *c == ' ').count();
|
||||||
|
let list_indentation = containers
|
||||||
|
.iter()
|
||||||
|
.map(|c| {
|
||||||
|
if let super::Container::List(indent) = c {
|
||||||
|
*indent
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
if ccount < blockquote_level || lcount < list_indentation {
|
||||||
|
let msg = if ccount < blockquote_level {
|
||||||
|
"doc quote missing `>` marker"
|
||||||
|
} else {
|
||||||
|
"doc list item missing indentation"
|
||||||
|
};
|
||||||
|
span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| {
|
||||||
|
if ccount == 0 && blockquote_level == 0 {
|
||||||
|
// simpler suggestion style for indentation
|
||||||
|
let indent = list_indentation - lcount;
|
||||||
|
diag.span_suggestion_with_style(
|
||||||
|
span.shrink_to_hi(),
|
||||||
|
"indent this line",
|
||||||
|
std::iter::repeat(" ").take(indent).join(""),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
SuggestionStyle::ShowAlways,
|
||||||
|
);
|
||||||
|
diag.help("if this is supposed to be its own paragraph, add a blank line");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut doc_start_range = &doc[range];
|
||||||
|
let mut suggested = String::new();
|
||||||
|
for c in containers {
|
||||||
|
let text = map_container_to_text(c);
|
||||||
|
if doc_start_range.starts_with(text) {
|
||||||
|
doc_start_range = &doc_start_range[text.len()..];
|
||||||
|
span = span
|
||||||
|
.with_lo(span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger")));
|
||||||
|
} else if matches!(c, super::Container::Blockquote)
|
||||||
|
&& let Some(i) = doc_start_range.find('>')
|
||||||
|
{
|
||||||
|
doc_start_range = &doc_start_range[i + 1..];
|
||||||
|
span =
|
||||||
|
span.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1));
|
||||||
|
} else {
|
||||||
|
suggested.push_str(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diag.span_suggestion_with_style(
|
||||||
|
span,
|
||||||
|
"add markers to start of line",
|
||||||
|
suggested,
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
SuggestionStyle::ShowAlways,
|
||||||
|
);
|
||||||
|
diag.help("if this not intended to be a quote at all, escape it with `\\>`");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC};
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
|
||||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||||
use clippy_utils::{is_doc_hidden, return_ty};
|
use clippy_utils::{is_doc_hidden, return_ty};
|
||||||
|
@ -6,15 +7,13 @@ use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC};
|
|
||||||
|
|
||||||
pub fn check(
|
pub fn check(
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
owner_id: OwnerId,
|
owner_id: OwnerId,
|
||||||
sig: FnSig<'_>,
|
sig: FnSig<'_>,
|
||||||
headers: DocHeaders,
|
headers: DocHeaders,
|
||||||
body_id: Option<BodyId>,
|
body_id: Option<BodyId>,
|
||||||
panic_span: Option<Span>,
|
panic_info: Option<(Span, bool)>,
|
||||||
check_private_items: bool,
|
check_private_items: bool,
|
||||||
) {
|
) {
|
||||||
if !check_private_items && !cx.effective_visibilities.is_exported(owner_id.def_id) {
|
if !check_private_items && !cx.effective_visibilities.is_exported(owner_id.def_id) {
|
||||||
|
@ -48,13 +47,13 @@ pub fn check(
|
||||||
),
|
),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
if !headers.panics && panic_span.is_some() {
|
if !headers.panics && panic_info.map_or(false, |el| !el.1) {
|
||||||
span_lint_and_note(
|
span_lint_and_note(
|
||||||
cx,
|
cx,
|
||||||
MISSING_PANICS_DOC,
|
MISSING_PANICS_DOC,
|
||||||
span,
|
span,
|
||||||
"docs for function which may panic missing `# Panics` section",
|
"docs for function which may panic missing `# Panics` section",
|
||||||
panic_span,
|
panic_info.map(|el| el.0),
|
||||||
"first possible panic found here",
|
"first possible panic found here",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
|
mod lazy_continuation;
|
||||||
use clippy_utils::attrs::is_doc_hidden;
|
use clippy_utils::attrs::is_doc_hidden;
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
use clippy_utils::visitors::Visitable;
|
use clippy_utils::visitors::Visitable;
|
||||||
use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args};
|
use clippy_utils::{in_constant, is_entrypoint_fn, is_trait_impl_item, method_chain_args};
|
||||||
use pulldown_cmark::Event::{
|
use pulldown_cmark::Event::{
|
||||||
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
|
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
|
||||||
};
|
};
|
||||||
use pulldown_cmark::Tag::{BlockQuote, CodeBlock, Heading, Item, Link, Paragraph};
|
use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph};
|
||||||
use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options};
|
use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options};
|
||||||
use rustc_ast::ast::Attribute;
|
use rustc_ast::ast::Attribute;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
|
@ -362,6 +363,63 @@ declare_clippy_lint! {
|
||||||
"docstrings exist but documentation is empty"
|
"docstrings exist but documentation is empty"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
///
|
||||||
|
/// In CommonMark Markdown, the language used to write doc comments, a
|
||||||
|
/// paragraph nested within a list or block quote does not need any line
|
||||||
|
/// after the first one to be indented or marked. The specification calls
|
||||||
|
/// this a "lazy paragraph continuation."
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
///
|
||||||
|
/// This is easy to write but hard to read. Lazy continuations makes
|
||||||
|
/// unintended markers hard to see, and make it harder to deduce the
|
||||||
|
/// document's intended structure.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// This table is probably intended to have two rows,
|
||||||
|
/// but it does not. It has zero rows, and is followed by
|
||||||
|
/// a block quote.
|
||||||
|
/// ```no_run
|
||||||
|
/// /// Range | Description
|
||||||
|
/// /// ----- | -----------
|
||||||
|
/// /// >= 1 | fully opaque
|
||||||
|
/// /// < 1 | partially see-through
|
||||||
|
/// fn set_opacity(opacity: f32) {}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Fix it by escaping the marker:
|
||||||
|
/// ```no_run
|
||||||
|
/// /// Range | Description
|
||||||
|
/// /// ----- | -----------
|
||||||
|
/// /// \>= 1 | fully opaque
|
||||||
|
/// /// < 1 | partially see-through
|
||||||
|
/// fn set_opacity(opacity: f32) {}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This example is actually intended to be a list:
|
||||||
|
/// ```no_run
|
||||||
|
/// /// * Do nothing.
|
||||||
|
/// /// * Then do something. Whatever it is needs done,
|
||||||
|
/// /// it should be done right now.
|
||||||
|
/// # fn do_stuff() {}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Fix it by indenting the list contents:
|
||||||
|
/// ```no_run
|
||||||
|
/// /// * Do nothing.
|
||||||
|
/// /// * Then do something. Whatever it is needs done,
|
||||||
|
/// /// it should be done right now.
|
||||||
|
/// # fn do_stuff() {}
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.80.0"]
|
||||||
|
pub DOC_LAZY_CONTINUATION,
|
||||||
|
style,
|
||||||
|
"require every line of a paragraph to be indented and marked"
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Documentation {
|
pub struct Documentation {
|
||||||
valid_idents: FxHashSet<String>,
|
valid_idents: FxHashSet<String>,
|
||||||
|
@ -388,6 +446,7 @@ impl_lint_pass!(Documentation => [
|
||||||
UNNECESSARY_SAFETY_DOC,
|
UNNECESSARY_SAFETY_DOC,
|
||||||
SUSPICIOUS_DOC_COMMENTS,
|
SUSPICIOUS_DOC_COMMENTS,
|
||||||
EMPTY_DOCS,
|
EMPTY_DOCS,
|
||||||
|
DOC_LAZY_CONTINUATION,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for Documentation {
|
impl<'tcx> LateLintPass<'tcx> for Documentation {
|
||||||
|
@ -402,14 +461,14 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
|
||||||
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
|
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
|
||||||
let body = cx.tcx.hir().body(body_id);
|
let body = cx.tcx.hir().body(body_id);
|
||||||
|
|
||||||
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
|
let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
|
||||||
missing_headers::check(
|
missing_headers::check(
|
||||||
cx,
|
cx,
|
||||||
item.owner_id,
|
item.owner_id,
|
||||||
sig,
|
sig,
|
||||||
headers,
|
headers,
|
||||||
Some(body_id),
|
Some(body_id),
|
||||||
panic_span,
|
panic_info,
|
||||||
self.check_private_items,
|
self.check_private_items,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -551,6 +610,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
|
||||||
cx,
|
cx,
|
||||||
valid_idents,
|
valid_idents,
|
||||||
parser.into_offset_iter(),
|
parser.into_offset_iter(),
|
||||||
|
&doc,
|
||||||
Fragments {
|
Fragments {
|
||||||
fragments: &fragments,
|
fragments: &fragments,
|
||||||
doc: &doc,
|
doc: &doc,
|
||||||
|
@ -560,6 +620,11 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
|
||||||
|
|
||||||
const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
|
const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
|
||||||
|
|
||||||
|
enum Container {
|
||||||
|
Blockquote,
|
||||||
|
List(usize),
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks parsed documentation.
|
/// Checks parsed documentation.
|
||||||
/// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`,
|
/// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`,
|
||||||
/// so lints here will generally access that information.
|
/// so lints here will generally access that information.
|
||||||
|
@ -569,6 +634,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
valid_idents: &FxHashSet<String>,
|
valid_idents: &FxHashSet<String>,
|
||||||
events: Events,
|
events: Events,
|
||||||
|
doc: &str,
|
||||||
fragments: Fragments<'_>,
|
fragments: Fragments<'_>,
|
||||||
) -> DocHeaders {
|
) -> DocHeaders {
|
||||||
// true if a safety header was found
|
// true if a safety header was found
|
||||||
|
@ -576,6 +642,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||||
let mut in_code = false;
|
let mut in_code = false;
|
||||||
let mut in_link = None;
|
let mut in_link = None;
|
||||||
let mut in_heading = false;
|
let mut in_heading = false;
|
||||||
|
let mut in_footnote_definition = false;
|
||||||
let mut is_rust = false;
|
let mut is_rust = false;
|
||||||
let mut no_test = false;
|
let mut no_test = false;
|
||||||
let mut ignore = false;
|
let mut ignore = false;
|
||||||
|
@ -586,7 +653,11 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||||
let mut code_level = 0;
|
let mut code_level = 0;
|
||||||
let mut blockquote_level = 0;
|
let mut blockquote_level = 0;
|
||||||
|
|
||||||
for (event, range) in events {
|
let mut containers = Vec::new();
|
||||||
|
|
||||||
|
let mut events = events.peekable();
|
||||||
|
|
||||||
|
while let Some((event, range)) = events.next() {
|
||||||
match event {
|
match event {
|
||||||
Html(tag) => {
|
Html(tag) => {
|
||||||
if tag.starts_with("<code") {
|
if tag.starts_with("<code") {
|
||||||
|
@ -599,8 +670,14 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||||
blockquote_level -= 1;
|
blockquote_level -= 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Start(BlockQuote) => blockquote_level += 1,
|
Start(BlockQuote) => {
|
||||||
End(BlockQuote) => blockquote_level -= 1,
|
blockquote_level += 1;
|
||||||
|
containers.push(Container::Blockquote);
|
||||||
|
},
|
||||||
|
End(BlockQuote) => {
|
||||||
|
blockquote_level -= 1;
|
||||||
|
containers.pop();
|
||||||
|
},
|
||||||
Start(CodeBlock(ref kind)) => {
|
Start(CodeBlock(ref kind)) => {
|
||||||
in_code = true;
|
in_code = true;
|
||||||
if let CodeBlockKind::Fenced(lang) = kind {
|
if let CodeBlockKind::Fenced(lang) = kind {
|
||||||
|
@ -633,6 +710,13 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||||
if let Start(Heading(_, _, _)) = event {
|
if let Start(Heading(_, _, _)) = event {
|
||||||
in_heading = true;
|
in_heading = true;
|
||||||
}
|
}
|
||||||
|
if let Start(Item) = event {
|
||||||
|
if let Some((_next_event, next_range)) = events.peek() {
|
||||||
|
containers.push(Container::List(next_range.start - range.start));
|
||||||
|
} else {
|
||||||
|
containers.push(Container::List(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
ticks_unbalanced = false;
|
ticks_unbalanced = false;
|
||||||
paragraph_range = range;
|
paragraph_range = range;
|
||||||
},
|
},
|
||||||
|
@ -640,6 +724,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||||
if let End(Heading(_, _, _)) = event {
|
if let End(Heading(_, _, _)) = event {
|
||||||
in_heading = false;
|
in_heading = false;
|
||||||
}
|
}
|
||||||
|
if let End(Item) = event {
|
||||||
|
containers.pop();
|
||||||
|
}
|
||||||
if ticks_unbalanced && let Some(span) = fragments.span(cx, paragraph_range.clone()) {
|
if ticks_unbalanced && let Some(span) = fragments.span(cx, paragraph_range.clone()) {
|
||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
cx,
|
cx,
|
||||||
|
@ -658,8 +745,26 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||||
}
|
}
|
||||||
text_to_check = Vec::new();
|
text_to_check = Vec::new();
|
||||||
},
|
},
|
||||||
|
Start(FootnoteDefinition(..)) => in_footnote_definition = true,
|
||||||
|
End(FootnoteDefinition(..)) => in_footnote_definition = false,
|
||||||
Start(_tag) | End(_tag) => (), // We don't care about other tags
|
Start(_tag) | End(_tag) => (), // We don't care about other tags
|
||||||
SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
|
SoftBreak | HardBreak => {
|
||||||
|
if !containers.is_empty()
|
||||||
|
&& let Some((_next_event, next_range)) = events.peek()
|
||||||
|
&& let Some(next_span) = fragments.span(cx, next_range.clone())
|
||||||
|
&& let Some(span) = fragments.span(cx, range.clone())
|
||||||
|
&& !in_footnote_definition
|
||||||
|
{
|
||||||
|
lazy_continuation::check(
|
||||||
|
cx,
|
||||||
|
doc,
|
||||||
|
range.end..next_range.start,
|
||||||
|
Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()),
|
||||||
|
&containers[..],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TaskListMarker(_) | Code(_) | Rule => (),
|
||||||
FootnoteReference(text) | Text(text) => {
|
FootnoteReference(text) | Text(text) => {
|
||||||
paragraph_range.end = range.end;
|
paragraph_range.end = range.end;
|
||||||
ticks_unbalanced |= text.contains('`') && !in_code;
|
ticks_unbalanced |= text.contains('`') && !in_code;
|
||||||
|
@ -701,6 +806,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||||
|
|
||||||
struct FindPanicUnwrap<'a, 'tcx> {
|
struct FindPanicUnwrap<'a, 'tcx> {
|
||||||
cx: &'a LateContext<'tcx>,
|
cx: &'a LateContext<'tcx>,
|
||||||
|
is_const: bool,
|
||||||
panic_span: Option<Span>,
|
panic_span: Option<Span>,
|
||||||
typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
||||||
}
|
}
|
||||||
|
@ -710,14 +816,15 @@ impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> {
|
||||||
cx: &'a LateContext<'tcx>,
|
cx: &'a LateContext<'tcx>,
|
||||||
typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
||||||
body: impl Visitable<'tcx>,
|
body: impl Visitable<'tcx>,
|
||||||
) -> Option<Span> {
|
) -> Option<(Span, bool)> {
|
||||||
let mut vis = Self {
|
let mut vis = Self {
|
||||||
cx,
|
cx,
|
||||||
|
is_const: false,
|
||||||
panic_span: None,
|
panic_span: None,
|
||||||
typeck_results,
|
typeck_results,
|
||||||
};
|
};
|
||||||
body.visit(&mut vis);
|
body.visit(&mut vis);
|
||||||
vis.panic_span
|
vis.panic_span.map(|el| (el, vis.is_const))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,6 +843,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
|
||||||
"assert" | "assert_eq" | "assert_ne"
|
"assert" | "assert_eq" | "assert_ne"
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
self.is_const = in_constant(self.cx, expr.hir_id);
|
||||||
self.panic_span = Some(macro_call.span);
|
self.panic_span = Some(macro_call.span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,9 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
|
||||||
too_large_for_stack: self.too_large_for_stack,
|
too_large_for_stack: self.too_large_for_stack,
|
||||||
};
|
};
|
||||||
|
|
||||||
ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v).consume_body(body).into_ok();
|
ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v)
|
||||||
|
.consume_body(body)
|
||||||
|
.into_ok();
|
||||||
|
|
||||||
for node in v.set {
|
for node in v.set {
|
||||||
span_lint_hir(
|
span_lint_hir(
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::macros::{find_format_args, format_args_inputs_span};
|
use clippy_utils::macros::{format_args_inputs_span, FormatArgsStorage};
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::{is_expn_of, path_def_id};
|
use clippy_utils::{is_expn_of, path_def_id};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
|
use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::{sym, ExpnId};
|
use rustc_span::{sym, ExpnId};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
|
@ -38,7 +38,17 @@ declare_clippy_lint! {
|
||||||
"using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
|
"using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
|
pub struct ExplicitWrite {
|
||||||
|
format_args: FormatArgsStorage,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExplicitWrite {
|
||||||
|
pub fn new(format_args: FormatArgsStorage) -> Self {
|
||||||
|
Self { format_args }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
|
@ -57,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
||||||
Some(sym::io_stderr) => ("stderr", "e"),
|
Some(sym::io_stderr) => ("stderr", "e"),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root()) else {
|
let Some(format_args) = self.format_args.get(cx, write_arg, ExpnId::root()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
||||||
};
|
};
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let inputs_snippet =
|
let inputs_snippet =
|
||||||
snippet_with_applicability(cx, format_args_inputs_span(&format_args), "..", &mut applicability);
|
snippet_with_applicability(cx, format_args_inputs_span(format_args), "..", &mut applicability);
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
EXPLICIT_WRITE,
|
EXPLICIT_WRITE,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::macros::{find_format_arg_expr, find_format_args, root_macro_call_first_node};
|
use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage};
|
||||||
use clippy_utils::source::{snippet_opt, snippet_with_context};
|
use clippy_utils::source::{snippet_opt, snippet_with_context};
|
||||||
use clippy_utils::sugg::Sugg;
|
use clippy_utils::sugg::Sugg;
|
||||||
use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
|
use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
|
||||||
|
@ -7,7 +7,7 @@ use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Expr, ExprKind};
|
use rustc_hir::{Expr, ExprKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
|
@ -39,13 +39,24 @@ declare_clippy_lint! {
|
||||||
"useless use of `format!`"
|
"useless use of `format!`"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
|
#[allow(clippy::module_name_repetitions)]
|
||||||
|
pub struct UselessFormat {
|
||||||
|
format_args: FormatArgsStorage,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UselessFormat {
|
||||||
|
pub fn new(format_args: FormatArgsStorage) -> Self {
|
||||||
|
Self { format_args }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_lint_pass!(UselessFormat => [USELESS_FORMAT]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||||
&& cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id)
|
&& cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id)
|
||||||
&& let Some(format_args) = find_format_args(cx, expr, macro_call.expn)
|
&& let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn)
|
||||||
{
|
{
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let call_site = macro_call.span;
|
let call_site = macro_call.span;
|
||||||
|
|
|
@ -3,8 +3,8 @@ use clippy_config::msrvs::{self, Msrv};
|
||||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||||
use clippy_utils::is_diag_trait_item;
|
use clippy_utils::is_diag_trait_item;
|
||||||
use clippy_utils::macros::{
|
use clippy_utils::macros::{
|
||||||
find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro,
|
find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro,
|
||||||
is_format_macro, is_panic, matching_root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall,
|
is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall,
|
||||||
};
|
};
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::ty::{implements_trait, is_type_lang_item};
|
use clippy_utils::ty::{implements_trait, is_type_lang_item};
|
||||||
|
@ -167,15 +167,18 @@ impl_lint_pass!(FormatArgs => [
|
||||||
UNUSED_FORMAT_SPECS,
|
UNUSED_FORMAT_SPECS,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
#[allow(clippy::struct_field_names)]
|
||||||
pub struct FormatArgs {
|
pub struct FormatArgs {
|
||||||
|
format_args: FormatArgsStorage,
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
ignore_mixed: bool,
|
ignore_mixed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatArgs {
|
impl FormatArgs {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self {
|
pub fn new(format_args: FormatArgsStorage, msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
format_args,
|
||||||
msrv,
|
msrv,
|
||||||
ignore_mixed: allow_mixed_uninlined_format_args,
|
ignore_mixed: allow_mixed_uninlined_format_args,
|
||||||
}
|
}
|
||||||
|
@ -186,13 +189,13 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||||
&& is_format_macro(cx, macro_call.def_id)
|
&& is_format_macro(cx, macro_call.def_id)
|
||||||
&& let Some(format_args) = find_format_args(cx, expr, macro_call.expn)
|
&& let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn)
|
||||||
{
|
{
|
||||||
let linter = FormatArgsExpr {
|
let linter = FormatArgsExpr {
|
||||||
cx,
|
cx,
|
||||||
expr,
|
expr,
|
||||||
macro_call: ¯o_call,
|
macro_call: ¯o_call,
|
||||||
format_args: &format_args,
|
format_args,
|
||||||
ignore_mixed: self.ignore_mixed,
|
ignore_mixed: self.ignore_mixed,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||||
use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node};
|
use clippy_utils::macros::{find_format_arg_expr, is_format_macro, root_macro_call_first_node, FormatArgsStorage};
|
||||||
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
|
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
|
||||||
use rustc_ast::{FormatArgsPiece, FormatTrait};
|
use rustc_ast::{FormatArgsPiece, FormatTrait};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -99,13 +99,15 @@ struct FormatTraitNames {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatImpl {
|
pub struct FormatImpl {
|
||||||
|
format_args: FormatArgsStorage,
|
||||||
// Whether we are inside Display or Debug trait impl - None for neither
|
// Whether we are inside Display or Debug trait impl - None for neither
|
||||||
format_trait_impl: Option<FormatTraitNames>,
|
format_trait_impl: Option<FormatTraitNames>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatImpl {
|
impl FormatImpl {
|
||||||
pub fn new() -> Self {
|
pub fn new(format_args: FormatArgsStorage) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
format_args,
|
||||||
format_trait_impl: None,
|
format_trait_impl: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,6 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
|
||||||
if let Some(format_trait_impl) = self.format_trait_impl {
|
if let Some(format_trait_impl) = self.format_trait_impl {
|
||||||
let linter = FormatImplExpr {
|
let linter = FormatImplExpr {
|
||||||
cx,
|
cx,
|
||||||
|
format_args: &self.format_args,
|
||||||
expr,
|
expr,
|
||||||
format_trait_impl,
|
format_trait_impl,
|
||||||
};
|
};
|
||||||
|
@ -141,6 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
|
||||||
|
|
||||||
struct FormatImplExpr<'a, 'tcx> {
|
struct FormatImplExpr<'a, 'tcx> {
|
||||||
cx: &'a LateContext<'tcx>,
|
cx: &'a LateContext<'tcx>,
|
||||||
|
format_args: &'a FormatArgsStorage,
|
||||||
expr: &'tcx Expr<'tcx>,
|
expr: &'tcx Expr<'tcx>,
|
||||||
format_trait_impl: FormatTraitNames,
|
format_trait_impl: FormatTraitNames,
|
||||||
}
|
}
|
||||||
|
@ -175,7 +179,7 @@ impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> {
|
||||||
if let Some(outer_macro) = root_macro_call_first_node(self.cx, self.expr)
|
if let Some(outer_macro) = root_macro_call_first_node(self.cx, self.expr)
|
||||||
&& let macro_def_id = outer_macro.def_id
|
&& let macro_def_id = outer_macro.def_id
|
||||||
&& is_format_macro(self.cx, macro_def_id)
|
&& is_format_macro(self.cx, macro_def_id)
|
||||||
&& let Some(format_args) = find_format_args(self.cx, self.expr, outer_macro.expn)
|
&& let Some(format_args) = self.format_args.get(self.cx, self.expr, outer_macro.expn)
|
||||||
{
|
{
|
||||||
for piece in &format_args.template {
|
for piece in &format_args.template {
|
||||||
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
||||||
|
|
|
@ -181,6 +181,9 @@ fn convert_to_from(
|
||||||
let from = snippet_opt(cx, self_ty.span)?;
|
let from = snippet_opt(cx, self_ty.span)?;
|
||||||
let into = snippet_opt(cx, target_ty.span)?;
|
let into = snippet_opt(cx, target_ty.span)?;
|
||||||
|
|
||||||
|
let return_type = matches!(sig.decl.output, FnRetTy::Return(_))
|
||||||
|
.then_some(String::from("Self"))
|
||||||
|
.unwrap_or_default();
|
||||||
let mut suggestions = vec![
|
let mut suggestions = vec![
|
||||||
// impl Into<T> for U -> impl From<T> for U
|
// impl Into<T> for U -> impl From<T> for U
|
||||||
// ~~~~ ~~~~
|
// ~~~~ ~~~~
|
||||||
|
@ -197,13 +200,10 @@ fn convert_to_from(
|
||||||
// fn into([mut] self) -> T -> fn into([mut] v: T) -> T
|
// fn into([mut] self) -> T -> fn into([mut] v: T) -> T
|
||||||
// ~~~~ ~~~~
|
// ~~~~ ~~~~
|
||||||
(self_ident.span, format!("val: {from}")),
|
(self_ident.span, format!("val: {from}")),
|
||||||
];
|
|
||||||
|
|
||||||
if let FnRetTy::Return(_) = sig.decl.output {
|
|
||||||
// fn into(self) -> T -> fn into(self) -> Self
|
// fn into(self) -> T -> fn into(self) -> Self
|
||||||
// ~ ~~~~
|
// ~ ~~~~
|
||||||
suggestions.push((sig.decl.output.span(), String::from("Self")));
|
(sig.decl.output.span(), return_type),
|
||||||
}
|
];
|
||||||
|
|
||||||
let mut finder = SelfFinder {
|
let mut finder = SelfFinder {
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::is_integer_literal;
|
|
||||||
use clippy_utils::sugg::Sugg;
|
use clippy_utils::sugg::Sugg;
|
||||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||||
|
use clippy_utils::{in_constant, is_integer_literal};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind};
|
use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
@ -47,6 +47,9 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) {
|
||||||
if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind
|
if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind
|
||||||
&& let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind
|
&& let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind
|
||||||
|
// do not lint in constant context, because the suggestion won't work.
|
||||||
|
// NB: keep this check until a new `const_trait_impl` is available and stablized.
|
||||||
|
&& !in_constant(cx, exp.hir_id)
|
||||||
|
|
||||||
// check if the first part of the path is some integer primitive
|
// check if the first part of the path is some integer primitive
|
||||||
&& let TyKind::Path(ty_qpath) = &ty.kind
|
&& let TyKind::Path(ty_qpath) = &ty.kind
|
||||||
|
|
|
@ -2,15 +2,17 @@ mod impl_trait_in_params;
|
||||||
mod misnamed_getters;
|
mod misnamed_getters;
|
||||||
mod must_use;
|
mod must_use;
|
||||||
mod not_unsafe_ptr_arg_deref;
|
mod not_unsafe_ptr_arg_deref;
|
||||||
|
mod renamed_function_params;
|
||||||
mod result;
|
mod result;
|
||||||
mod too_many_arguments;
|
mod too_many_arguments;
|
||||||
mod too_many_lines;
|
mod too_many_lines;
|
||||||
|
|
||||||
|
use clippy_utils::def_path_def_ids;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::intravisit;
|
use rustc_hir::intravisit;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::def_id::LocalDefId;
|
use rustc_span::def_id::{DefIdSet, LocalDefId};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
|
@ -359,13 +361,51 @@ declare_clippy_lint! {
|
||||||
"`impl Trait` is used in the function's parameters"
|
"`impl Trait` is used in the function's parameters"
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
declare_clippy_lint! {
|
||||||
#[allow(clippy::struct_field_names)]
|
/// ### What it does
|
||||||
|
/// Lints when the name of function parameters from trait impl is
|
||||||
|
/// different than its default implementation.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// Using the default name for parameters of a trait method is often
|
||||||
|
/// more desirable for consistency's sake.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// struct A(u32);
|
||||||
|
///
|
||||||
|
/// impl PartialEq for A {
|
||||||
|
/// fn eq(&self, b: &Self) -> bool {
|
||||||
|
/// self.0 == b.0
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// struct A(u32);
|
||||||
|
///
|
||||||
|
/// impl PartialEq for A {
|
||||||
|
/// fn eq(&self, other: &Self) -> bool {
|
||||||
|
/// self.0 == other.0
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.74.0"]
|
||||||
|
pub RENAMED_FUNCTION_PARAMS,
|
||||||
|
restriction,
|
||||||
|
"renamed function parameters in trait implementation"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Functions {
|
pub struct Functions {
|
||||||
too_many_arguments_threshold: u64,
|
too_many_arguments_threshold: u64,
|
||||||
too_many_lines_threshold: u64,
|
too_many_lines_threshold: u64,
|
||||||
large_error_threshold: u64,
|
large_error_threshold: u64,
|
||||||
avoid_breaking_exported_api: bool,
|
avoid_breaking_exported_api: bool,
|
||||||
|
allow_renamed_params_for: Vec<String>,
|
||||||
|
/// A set of resolved `def_id` of traits that are configured to allow
|
||||||
|
/// function params renaming.
|
||||||
|
trait_ids: DefIdSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Functions {
|
impl Functions {
|
||||||
|
@ -374,12 +414,15 @@ impl Functions {
|
||||||
too_many_lines_threshold: u64,
|
too_many_lines_threshold: u64,
|
||||||
large_error_threshold: u64,
|
large_error_threshold: u64,
|
||||||
avoid_breaking_exported_api: bool,
|
avoid_breaking_exported_api: bool,
|
||||||
|
allow_renamed_params_for: Vec<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
too_many_arguments_threshold,
|
too_many_arguments_threshold,
|
||||||
too_many_lines_threshold,
|
too_many_lines_threshold,
|
||||||
large_error_threshold,
|
large_error_threshold,
|
||||||
avoid_breaking_exported_api,
|
avoid_breaking_exported_api,
|
||||||
|
allow_renamed_params_for,
|
||||||
|
trait_ids: DefIdSet::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,6 +438,7 @@ impl_lint_pass!(Functions => [
|
||||||
RESULT_LARGE_ERR,
|
RESULT_LARGE_ERR,
|
||||||
MISNAMED_GETTERS,
|
MISNAMED_GETTERS,
|
||||||
IMPL_TRAIT_IN_PARAMS,
|
IMPL_TRAIT_IN_PARAMS,
|
||||||
|
RENAMED_FUNCTION_PARAMS,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for Functions {
|
impl<'tcx> LateLintPass<'tcx> for Functions {
|
||||||
|
@ -424,6 +468,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
|
||||||
must_use::check_impl_item(cx, item);
|
must_use::check_impl_item(cx, item);
|
||||||
result::check_impl_item(cx, item, self.large_error_threshold);
|
result::check_impl_item(cx, item, self.large_error_threshold);
|
||||||
impl_trait_in_params::check_impl_item(cx, item);
|
impl_trait_in_params::check_impl_item(cx, item);
|
||||||
|
renamed_function_params::check_impl_item(cx, item, &self.trait_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
|
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
|
||||||
|
@ -433,4 +478,12 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
|
||||||
result::check_trait_item(cx, item, self.large_error_threshold);
|
result::check_trait_item(cx, item, self.large_error_threshold);
|
||||||
impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api);
|
impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||||
|
for path in &self.allow_renamed_params_for {
|
||||||
|
let path_segments: Vec<&str> = path.split("::").collect();
|
||||||
|
let ids = def_path_def_ids(cx, &path_segments);
|
||||||
|
self.trait_ids.extend(ids);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
110
clippy_lints/src/functions/renamed_function_params.rs
Normal file
110
clippy_lints/src/functions/renamed_function_params.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use rustc_errors::{Applicability, MultiSpan};
|
||||||
|
use rustc_hir::def_id::{DefId, DefIdSet};
|
||||||
|
use rustc_hir::hir_id::OwnerId;
|
||||||
|
use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef};
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_span::symbol::{kw, Ident, Symbol};
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
use super::RENAMED_FUNCTION_PARAMS;
|
||||||
|
|
||||||
|
pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored_traits: &DefIdSet) {
|
||||||
|
if !item.span.from_expansion()
|
||||||
|
&& let ImplItemKind::Fn(_, body_id) = item.kind
|
||||||
|
&& let parent_node = cx.tcx.parent_hir_node(item.hir_id())
|
||||||
|
&& let Node::Item(parent_item) = parent_node
|
||||||
|
&& let ItemKind::Impl(Impl {
|
||||||
|
items,
|
||||||
|
of_trait: Some(trait_ref),
|
||||||
|
..
|
||||||
|
}) = &parent_item.kind
|
||||||
|
&& let Some(did) = trait_item_def_id_of_impl(items, item.owner_id)
|
||||||
|
&& !is_from_ignored_trait(trait_ref, ignored_traits)
|
||||||
|
{
|
||||||
|
let mut param_idents_iter = cx.tcx.hir().body_param_names(body_id);
|
||||||
|
let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied();
|
||||||
|
|
||||||
|
let renames = RenamedFnArgs::new(&mut default_param_idents_iter, &mut param_idents_iter);
|
||||||
|
if !renames.0.is_empty() {
|
||||||
|
let multi_span = renames.multi_span();
|
||||||
|
let plural = if renames.0.len() == 1 { "" } else { "s" };
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
RENAMED_FUNCTION_PARAMS,
|
||||||
|
multi_span,
|
||||||
|
format!("renamed function parameter{plural} of trait impl"),
|
||||||
|
|diag| {
|
||||||
|
diag.multipart_suggestion(
|
||||||
|
format!("consider using the default name{plural}"),
|
||||||
|
renames.0,
|
||||||
|
Applicability::Unspecified,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RenamedFnArgs(Vec<(Span, String)>);
|
||||||
|
|
||||||
|
impl RenamedFnArgs {
|
||||||
|
/// Comparing between an iterator of default names and one with current names,
|
||||||
|
/// then collect the ones that got renamed.
|
||||||
|
fn new<I, T>(default_names: &mut I, current_names: &mut T) -> Self
|
||||||
|
where
|
||||||
|
I: Iterator<Item = Ident>,
|
||||||
|
T: Iterator<Item = Ident>,
|
||||||
|
{
|
||||||
|
let mut renamed: Vec<(Span, String)> = vec![];
|
||||||
|
|
||||||
|
debug_assert!(default_names.size_hint() == current_names.size_hint());
|
||||||
|
while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) {
|
||||||
|
let current_name = cur_name.name;
|
||||||
|
let default_name = def_name.name;
|
||||||
|
if is_unused_or_empty_symbol(current_name) || is_unused_or_empty_symbol(default_name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if current_name != default_name {
|
||||||
|
renamed.push((cur_name.span, default_name.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self(renamed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multi_span(&self) -> MultiSpan {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.map(|(span, _)| span)
|
||||||
|
.copied()
|
||||||
|
.collect::<Vec<Span>>()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_unused_or_empty_symbol(symbol: Symbol) -> bool {
|
||||||
|
// FIXME: `body_param_names` currently returning empty symbols for `wild` as well,
|
||||||
|
// so we need to check if the symbol is empty first.
|
||||||
|
// Therefore the check of whether it's equal to [`kw::Underscore`] has no use for now,
|
||||||
|
// but it would be nice to keep it here just to be future-proof.
|
||||||
|
symbol.is_empty() || symbol == kw::Underscore || symbol.as_str().starts_with('_')
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item.
|
||||||
|
fn trait_item_def_id_of_impl(items: &[ImplItemRef], target: OwnerId) -> Option<DefId> {
|
||||||
|
items.iter().find_map(|item| {
|
||||||
|
if item.id.owner_id == target {
|
||||||
|
item.trait_item_def_id
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_from_ignored_trait(of_trait: &TraitRef<'_>, ignored_traits: &DefIdSet) -> bool {
|
||||||
|
let Some(trait_did) = of_trait.trait_def_id() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
ignored_traits.contains(&trait_did)
|
||||||
|
}
|
|
@ -4,8 +4,8 @@ use rustc_hir::intravisit::FnKind;
|
||||||
use rustc_hir::{Body, FnDecl};
|
use rustc_hir::{Body, FnDecl};
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind};
|
|
||||||
use rustc_middle::ty::print::PrintTraitRefExt;
|
use rustc_middle::ty::print::PrintTraitRefExt;
|
||||||
|
use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
use rustc_span::def_id::LocalDefId;
|
use rustc_span::def_id::LocalDefId;
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
|
|
@ -61,11 +61,6 @@ extern crate clippy_utils;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate declare_clippy_lint;
|
extern crate declare_clippy_lint;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
|
||||||
use rustc_lint::{Lint, LintId};
|
|
||||||
|
|
||||||
#[cfg(feature = "internal")]
|
#[cfg(feature = "internal")]
|
||||||
pub mod deprecated_lints;
|
pub mod deprecated_lints;
|
||||||
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
|
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
|
||||||
|
@ -199,6 +194,7 @@ mod lifetimes;
|
||||||
mod lines_filter_map_ok;
|
mod lines_filter_map_ok;
|
||||||
mod literal_representation;
|
mod literal_representation;
|
||||||
mod loops;
|
mod loops;
|
||||||
|
mod macro_metavars_in_unsafe;
|
||||||
mod macro_use;
|
mod macro_use;
|
||||||
mod main_recursion;
|
mod main_recursion;
|
||||||
mod manual_assert;
|
mod manual_assert;
|
||||||
|
@ -385,6 +381,10 @@ mod zero_sized_map_values;
|
||||||
// end lints modules, do not remove this comment, it’s used in `update_lints`
|
// end lints modules, do not remove this comment, it’s used in `update_lints`
|
||||||
|
|
||||||
use clippy_config::{get_configuration_metadata, Conf};
|
use clippy_config::{get_configuration_metadata, Conf};
|
||||||
|
use clippy_utils::macros::FormatArgsStorage;
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
|
use rustc_lint::{Lint, LintId};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
/// Register all pre expansion lints
|
/// Register all pre expansion lints
|
||||||
///
|
///
|
||||||
|
@ -597,9 +597,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
ref allowed_duplicate_crates,
|
ref allowed_duplicate_crates,
|
||||||
allow_comparison_to_zero,
|
allow_comparison_to_zero,
|
||||||
ref allowed_prefixes,
|
ref allowed_prefixes,
|
||||||
|
ref allow_renamed_params_for,
|
||||||
|
|
||||||
blacklisted_names: _,
|
blacklisted_names: _,
|
||||||
cyclomatic_complexity_threshold: _,
|
cyclomatic_complexity_threshold: _,
|
||||||
|
warn_unsafe_macro_metavars_in_private_macros,
|
||||||
} = *conf;
|
} = *conf;
|
||||||
let msrv = || msrv.clone();
|
let msrv = || msrv.clone();
|
||||||
|
|
||||||
|
@ -616,6 +618,14 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let format_args_storage = FormatArgsStorage::default();
|
||||||
|
let format_args = format_args_storage.clone();
|
||||||
|
store.register_early_pass(move || {
|
||||||
|
Box::new(utils::format_args_collector::FormatArgsCollector::new(
|
||||||
|
format_args.clone(),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
// all the internal lints
|
// all the internal lints
|
||||||
#[cfg(feature = "internal")]
|
#[cfg(feature = "internal")]
|
||||||
{
|
{
|
||||||
|
@ -656,7 +666,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
.collect(),
|
.collect(),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
store.register_early_pass(|| Box::<utils::format_args_collector::FormatArgsCollector>::default());
|
|
||||||
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
|
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
|
||||||
store.register_late_pass(|_| Box::new(utils::author::Author));
|
store.register_late_pass(|_| Box::new(utils::author::Author));
|
||||||
store.register_late_pass(move |_| {
|
store.register_late_pass(move |_| {
|
||||||
|
@ -698,6 +707,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
|
store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
|
||||||
store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
|
store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
|
||||||
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
|
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
|
||||||
|
let format_args = format_args_storage.clone();
|
||||||
store.register_late_pass(move |_| {
|
store.register_late_pass(move |_| {
|
||||||
Box::new(methods::Methods::new(
|
Box::new(methods::Methods::new(
|
||||||
avoid_breaking_exported_api,
|
avoid_breaking_exported_api,
|
||||||
|
@ -705,6 +715,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
allow_expect_in_tests,
|
allow_expect_in_tests,
|
||||||
allow_unwrap_in_tests,
|
allow_unwrap_in_tests,
|
||||||
allowed_dotfiles.clone(),
|
allowed_dotfiles.clone(),
|
||||||
|
format_args.clone(),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv())));
|
store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv())));
|
||||||
|
@ -769,7 +780,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
store.register_late_pass(|_| Box::<regex::Regex>::default());
|
store.register_late_pass(|_| Box::<regex::Regex>::default());
|
||||||
store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone())));
|
store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone())));
|
||||||
store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
|
store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
|
||||||
store.register_late_pass(|_| Box::new(format::UselessFormat));
|
let format_args = format_args_storage.clone();
|
||||||
|
store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone())));
|
||||||
store.register_late_pass(|_| Box::new(swap::Swap));
|
store.register_late_pass(|_| Box::new(swap::Swap));
|
||||||
store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional));
|
store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional));
|
||||||
store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default());
|
store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default());
|
||||||
|
@ -780,6 +792,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
too_many_lines_threshold,
|
too_many_lines_threshold,
|
||||||
large_error_threshold,
|
large_error_threshold,
|
||||||
avoid_breaking_exported_api,
|
avoid_breaking_exported_api,
|
||||||
|
allow_renamed_params_for.clone(),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
store.register_late_pass(move |_| Box::new(doc::Documentation::new(doc_valid_idents, check_private_items)));
|
store.register_late_pass(move |_| Box::new(doc::Documentation::new(doc_valid_idents, check_private_items)));
|
||||||
|
@ -793,7 +806,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
|
store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
|
||||||
store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));
|
store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));
|
||||||
store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
|
store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
|
||||||
store.register_late_pass(|_| Box::new(explicit_write::ExplicitWrite));
|
let format_args = format_args_storage.clone();
|
||||||
|
store.register_late_pass(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone())));
|
||||||
store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue));
|
store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue));
|
||||||
store.register_late_pass(move |tcx| {
|
store.register_late_pass(move |tcx| {
|
||||||
Box::new(pass_by_ref_or_value::PassByRefOrValue::new(
|
Box::new(pass_by_ref_or_value::PassByRefOrValue::new(
|
||||||
|
@ -835,7 +849,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone())));
|
store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone())));
|
||||||
store.register_early_pass(|| Box::new(reference::DerefAddrOf));
|
store.register_early_pass(|| Box::new(reference::DerefAddrOf));
|
||||||
store.register_early_pass(|| Box::new(double_parens::DoubleParens));
|
store.register_early_pass(|| Box::new(double_parens::DoubleParens));
|
||||||
store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new()));
|
let format_args = format_args_storage.clone();
|
||||||
|
store.register_late_pass(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone())));
|
||||||
store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
|
store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
|
||||||
store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
|
store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
|
||||||
store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
|
store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
|
||||||
|
@ -961,8 +976,14 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
accept_comment_above_attributes,
|
accept_comment_above_attributes,
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
store
|
let format_args = format_args_storage.clone();
|
||||||
.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined_format_args)));
|
store.register_late_pass(move |_| {
|
||||||
|
Box::new(format_args::FormatArgs::new(
|
||||||
|
format_args.clone(),
|
||||||
|
msrv(),
|
||||||
|
allow_mixed_uninlined_format_args,
|
||||||
|
))
|
||||||
|
});
|
||||||
store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
|
store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
|
||||||
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
|
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
|
||||||
store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
|
store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
|
||||||
|
@ -973,7 +994,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
|
store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
|
||||||
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
|
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
|
||||||
store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
|
store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
|
||||||
store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests)));
|
let format_args = format_args_storage.clone();
|
||||||
|
store.register_late_pass(move |_| Box::new(write::Write::new(format_args.clone(), allow_print_in_tests)));
|
||||||
store.register_late_pass(move |_| {
|
store.register_late_pass(move |_| {
|
||||||
Box::new(cargo::Cargo {
|
Box::new(cargo::Cargo {
|
||||||
ignore_publish: cargo_ignore_publish,
|
ignore_publish: cargo_ignore_publish,
|
||||||
|
@ -1136,6 +1158,12 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects));
|
store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects));
|
||||||
store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault));
|
store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault));
|
||||||
store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed));
|
store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed));
|
||||||
|
store.register_late_pass(move |_| {
|
||||||
|
Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe {
|
||||||
|
warn_unsafe_macro_metavars_in_private_macros,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
});
|
||||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,12 +60,9 @@ fn check_for_mutation(
|
||||||
span_low: None,
|
span_low: None,
|
||||||
span_high: None,
|
span_high: None,
|
||||||
};
|
};
|
||||||
ExprUseVisitor::for_clippy(
|
ExprUseVisitor::for_clippy(cx, body.hir_id.owner.def_id, &mut delegate)
|
||||||
cx,
|
.walk_expr(body)
|
||||||
body.hir_id.owner.def_id,
|
.into_ok();
|
||||||
&mut delegate,
|
|
||||||
)
|
|
||||||
.walk_expr(body).into_ok();
|
|
||||||
|
|
||||||
delegate.mutation_span()
|
delegate.mutation_span()
|
||||||
}
|
}
|
||||||
|
|
256
clippy_lints/src/macro_metavars_in_unsafe.rs
Normal file
256
clippy_lints/src/macro_metavars_in_unsafe.rs
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
use std::collections::btree_map::Entry;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||||
|
use clippy_utils::is_lint_allowed;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use rustc_hir::def_id::LocalDefId;
|
||||||
|
use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor};
|
||||||
|
use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_session::impl_lint_pass;
|
||||||
|
use rustc_span::{sym, Span, SyntaxContext};
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Looks for macros that expand metavariables in an unsafe block.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// This hides an unsafe block and allows the user of the macro to write unsafe code without an explicit
|
||||||
|
/// unsafe block at callsite, making it possible to perform unsafe operations in seemingly safe code.
|
||||||
|
///
|
||||||
|
/// The macro should be restructured so that these metavariables are referenced outside of unsafe blocks
|
||||||
|
/// and that the usual unsafety checks apply to the macro argument.
|
||||||
|
///
|
||||||
|
/// This is usually done by binding it to a variable outside of the unsafe block
|
||||||
|
/// and then using that variable inside of the block as shown in the example, or by referencing it a second time
|
||||||
|
/// in a safe context, e.g. `if false { $expr }`.
|
||||||
|
///
|
||||||
|
/// ### Known limitations
|
||||||
|
/// Due to how macros are represented in the compiler at the time Clippy runs its lints,
|
||||||
|
/// it's not possible to look for metavariables in macro definitions directly.
|
||||||
|
///
|
||||||
|
/// Instead, this lint looks at expansions of macros.
|
||||||
|
/// This leads to false negatives for macros that are never actually invoked.
|
||||||
|
///
|
||||||
|
/// By default, this lint is rather conservative and will only emit warnings on publicly-exported
|
||||||
|
/// macros from the same crate, because oftentimes private internal macros are one-off macros where
|
||||||
|
/// this lint would just be noise (e.g. macros that generate `impl` blocks).
|
||||||
|
/// The default behavior should help with preventing a high number of such false positives,
|
||||||
|
/// however it can be configured to also emit warnings in private macros if desired.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```no_run
|
||||||
|
/// /// Gets the first element of a slice
|
||||||
|
/// macro_rules! first {
|
||||||
|
/// ($slice:expr) => {
|
||||||
|
/// unsafe {
|
||||||
|
/// let slice = $slice; // ⚠️ expansion inside of `unsafe {}`
|
||||||
|
///
|
||||||
|
/// assert!(!slice.is_empty());
|
||||||
|
/// // SAFETY: slice is checked to have at least one element
|
||||||
|
/// slice.first().unwrap_unchecked()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(*first!(&[1i32]), 1);
|
||||||
|
///
|
||||||
|
/// // This will compile as a consequence (note the lack of `unsafe {}`)
|
||||||
|
/// assert_eq!(*first!(std::hint::unreachable_unchecked() as &[i32]), 1);
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```compile_fail
|
||||||
|
/// macro_rules! first {
|
||||||
|
/// ($slice:expr) => {{
|
||||||
|
/// let slice = $slice; // ✅ outside of `unsafe {}`
|
||||||
|
/// unsafe {
|
||||||
|
/// assert!(!slice.is_empty());
|
||||||
|
/// // SAFETY: slice is checked to have at least one element
|
||||||
|
/// slice.first().unwrap_unchecked()
|
||||||
|
/// }
|
||||||
|
/// }}
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(*first!(&[1]), 1);
|
||||||
|
///
|
||||||
|
/// // This won't compile:
|
||||||
|
/// assert_eq!(*first!(std::hint::unreachable_unchecked() as &[i32]), 1);
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.80.0"]
|
||||||
|
pub MACRO_METAVARS_IN_UNSAFE,
|
||||||
|
suspicious,
|
||||||
|
"expanding macro metavariables in an unsafe block"
|
||||||
|
}
|
||||||
|
impl_lint_pass!(ExprMetavarsInUnsafe => [MACRO_METAVARS_IN_UNSAFE]);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum MetavarState {
|
||||||
|
ReferencedInUnsafe { unsafe_blocks: Vec<HirId> },
|
||||||
|
ReferencedInSafe,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ExprMetavarsInUnsafe {
|
||||||
|
pub warn_unsafe_macro_metavars_in_private_macros: bool,
|
||||||
|
/// A metavariable can be expanded more than once, potentially across multiple bodies, so it
|
||||||
|
/// requires some state kept across HIR nodes to make it possible to delay a warning
|
||||||
|
/// and later undo:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// macro_rules! x {
|
||||||
|
/// ($v:expr) => {
|
||||||
|
/// unsafe { $v; } // unsafe context, it might be possible to emit a warning here, so add it to the map
|
||||||
|
///
|
||||||
|
/// $v; // `$v` expanded another time but in a safe context, set to ReferencedInSafe to suppress
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub metavar_expns: BTreeMap<Span, MetavarState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BodyVisitor<'a, 'tcx> {
|
||||||
|
/// Stack of unsafe blocks -- the top item always represents the last seen unsafe block from
|
||||||
|
/// within a relevant macro.
|
||||||
|
macro_unsafe_blocks: Vec<HirId>,
|
||||||
|
/// When this is >0, it means that the node currently being visited is "within" a
|
||||||
|
/// macro definition. This is not necessary for correctness, it merely helps reduce the number
|
||||||
|
/// of spans we need to insert into the map, since only spans from macros are relevant.
|
||||||
|
expn_depth: u32,
|
||||||
|
cx: &'a LateContext<'tcx>,
|
||||||
|
lint: &'a mut ExprMetavarsInUnsafe,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
|
||||||
|
(cx.effective_visibilities.is_exported(def_id) || cx.tcx.has_attr(def_id, sym::macro_export))
|
||||||
|
&& !cx.tcx.is_doc_hidden(def_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> {
|
||||||
|
fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
|
||||||
|
let from_expn = s.span.from_expansion();
|
||||||
|
if from_expn {
|
||||||
|
self.expn_depth += 1;
|
||||||
|
}
|
||||||
|
walk_stmt(self, s);
|
||||||
|
if from_expn {
|
||||||
|
self.expn_depth -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
|
||||||
|
let ctxt = e.span.ctxt();
|
||||||
|
|
||||||
|
if let ExprKind::Block(block, _) = e.kind
|
||||||
|
&& let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules
|
||||||
|
&& !ctxt.is_root()
|
||||||
|
&& let Some(macro_def_id) = ctxt.outer_expn_data().macro_def_id
|
||||||
|
&& let Some(macro_def_id) = macro_def_id.as_local()
|
||||||
|
&& (self.lint.warn_unsafe_macro_metavars_in_private_macros || is_public_macro(self.cx, macro_def_id))
|
||||||
|
{
|
||||||
|
self.macro_unsafe_blocks.push(block.hir_id);
|
||||||
|
walk_block(self, block);
|
||||||
|
self.macro_unsafe_blocks.pop();
|
||||||
|
} else if ctxt.is_root() && self.expn_depth > 0 {
|
||||||
|
let unsafe_block = self.macro_unsafe_blocks.last().copied();
|
||||||
|
|
||||||
|
match (self.lint.metavar_expns.entry(e.span), unsafe_block) {
|
||||||
|
(Entry::Vacant(e), None) => {
|
||||||
|
e.insert(MetavarState::ReferencedInSafe);
|
||||||
|
},
|
||||||
|
(Entry::Vacant(e), Some(unsafe_block)) => {
|
||||||
|
e.insert(MetavarState::ReferencedInUnsafe {
|
||||||
|
unsafe_blocks: vec![unsafe_block],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(Entry::Occupied(mut e), None) => {
|
||||||
|
if let MetavarState::ReferencedInUnsafe { .. } = *e.get() {
|
||||||
|
e.insert(MetavarState::ReferencedInSafe);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(Entry::Occupied(mut e), Some(unsafe_block)) => {
|
||||||
|
if let MetavarState::ReferencedInUnsafe { unsafe_blocks } = e.get_mut()
|
||||||
|
&& !unsafe_blocks.contains(&unsafe_block)
|
||||||
|
{
|
||||||
|
unsafe_blocks.push(unsafe_block);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: No need to visit descendant nodes. They're guaranteed to represent the same
|
||||||
|
// metavariable
|
||||||
|
} else {
|
||||||
|
walk_expr(self, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe {
|
||||||
|
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx rustc_hir::Body<'tcx>) {
|
||||||
|
if is_lint_allowed(cx, MACRO_METAVARS_IN_UNSAFE, body.value.hir_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This BodyVisitor is separate and not part of the lint pass because there is no
|
||||||
|
// `check_stmt_post` on `(Late)LintPass`, which we'd need to detect when we're leaving a macro span
|
||||||
|
|
||||||
|
let mut vis = BodyVisitor {
|
||||||
|
#[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning
|
||||||
|
expn_depth: if body.value.span.from_expansion() { 1 } else { 0 },
|
||||||
|
macro_unsafe_blocks: Vec::new(),
|
||||||
|
lint: self,
|
||||||
|
cx
|
||||||
|
};
|
||||||
|
vis.visit_body(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
|
||||||
|
// Aggregate all unsafe blocks from all spans:
|
||||||
|
// ```
|
||||||
|
// macro_rules! x {
|
||||||
|
// ($w:expr, $x:expr, $y:expr) => { $w; unsafe { $w; $x; }; unsafe { $x; $y; }; }
|
||||||
|
// }
|
||||||
|
// $w: [] (unsafe#0 is never added because it was referenced in a safe context)
|
||||||
|
// $x: [unsafe#0, unsafe#1]
|
||||||
|
// $y: [unsafe#1]
|
||||||
|
// ```
|
||||||
|
// We want to lint unsafe blocks #0 and #1
|
||||||
|
let bad_unsafe_blocks = self
|
||||||
|
.metavar_expns
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(_, state)| match state {
|
||||||
|
MetavarState::ReferencedInUnsafe { unsafe_blocks } => Some(unsafe_blocks.as_slice()),
|
||||||
|
MetavarState::ReferencedInSafe => None,
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.copied()
|
||||||
|
.map(|id| {
|
||||||
|
// Remove the syntax context to hide "in this macro invocation" in the diagnostic.
|
||||||
|
// The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything
|
||||||
|
// related to the callsite.
|
||||||
|
let span = cx.tcx.hir().span(id);
|
||||||
|
|
||||||
|
(id, Span::new(span.lo(), span.hi(), SyntaxContext::root(), None))
|
||||||
|
})
|
||||||
|
.dedup_by(|&(_, a), &(_, b)| a == b);
|
||||||
|
|
||||||
|
for (id, span) in bad_unsafe_blocks {
|
||||||
|
span_lint_hir_and_then(
|
||||||
|
cx,
|
||||||
|
MACRO_METAVARS_IN_UNSAFE,
|
||||||
|
id,
|
||||||
|
span,
|
||||||
|
"this macro expands metavariables in an unsafe block",
|
||||||
|
|diag| {
|
||||||
|
diag.note("this allows the user of the macro to write unsafe code outside of an unsafe block");
|
||||||
|
diag.help(
|
||||||
|
"consider expanding any metavariables outside of this block, e.g. by storing them in a variable",
|
||||||
|
);
|
||||||
|
diag.help(
|
||||||
|
"... or also expand referenced metavariables in a safe context to require an unsafe block at callsite",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -611,15 +611,22 @@ impl<'tcx> BinaryOp<'tcx> {
|
||||||
|
|
||||||
/// The clamp meta pattern is a pattern shared between many (but not all) patterns.
|
/// The clamp meta pattern is a pattern shared between many (but not all) patterns.
|
||||||
/// In summary, this pattern consists of two if statements that meet many criteria,
|
/// In summary, this pattern consists of two if statements that meet many criteria,
|
||||||
|
///
|
||||||
/// - binary operators that are one of [`>`, `<`, `>=`, `<=`].
|
/// - binary operators that are one of [`>`, `<`, `>=`, `<=`].
|
||||||
|
///
|
||||||
/// - Both binary statements must have a shared argument
|
/// - Both binary statements must have a shared argument
|
||||||
|
///
|
||||||
/// - Which can appear on the left or right side of either statement
|
/// - Which can appear on the left or right side of either statement
|
||||||
|
///
|
||||||
/// - The binary operators must define a finite range for the shared argument. To put this in
|
/// - The binary operators must define a finite range for the shared argument. To put this in
|
||||||
/// the terms of Rust `std` library, the following ranges are acceptable
|
/// the terms of Rust `std` library, the following ranges are acceptable
|
||||||
|
///
|
||||||
/// - `Range`
|
/// - `Range`
|
||||||
/// - `RangeInclusive`
|
/// - `RangeInclusive`
|
||||||
|
///
|
||||||
/// And all other range types are not accepted. For the purposes of `clamp` it's irrelevant
|
/// And all other range types are not accepted. For the purposes of `clamp` it's irrelevant
|
||||||
/// whether the range is inclusive or not, the output is the same.
|
/// whether the range is inclusive or not, the output is the same.
|
||||||
|
///
|
||||||
/// - The result of each if statement must be equal to the argument unique to that if statement. The
|
/// - The result of each if statement must be equal to the argument unique to that if statement. The
|
||||||
/// result can not be the shared argument in either case.
|
/// result can not be the shared argument in either case.
|
||||||
fn is_clamp_meta_pattern<'tcx>(
|
fn is_clamp_meta_pattern<'tcx>(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash};
|
use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash};
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use core::{iter, slice};
|
use core::{iter, slice};
|
||||||
|
@ -9,9 +9,9 @@ use rustc_errors::Applicability;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd};
|
use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd};
|
||||||
use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::{LateContext, LintContext};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_span::{ErrorGuaranteed, Symbol};
|
use rustc_span::{ErrorGuaranteed, Span, Symbol};
|
||||||
|
|
||||||
use super::MATCH_SAME_ARMS;
|
use super::MATCH_SAME_ARMS;
|
||||||
|
|
||||||
|
@ -110,20 +110,22 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
||||||
&& check_same_body()
|
&& check_same_body()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut appl = Applicability::MaybeIncorrect;
|
||||||
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
|
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
|
||||||
for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
|
for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
|
||||||
if matches!(arm2.pat.kind, PatKind::Wild) {
|
if matches!(arm2.pat.kind, PatKind::Wild) {
|
||||||
if !cx.tcx.features().non_exhaustive_omitted_patterns_lint
|
if !cx.tcx.features().non_exhaustive_omitted_patterns_lint
|
||||||
|| is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id)
|
|| is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id)
|
||||||
{
|
{
|
||||||
|
let arm_span = adjusted_arm_span(cx, arm1.span);
|
||||||
span_lint_hir_and_then(
|
span_lint_hir_and_then(
|
||||||
cx,
|
cx,
|
||||||
MATCH_SAME_ARMS,
|
MATCH_SAME_ARMS,
|
||||||
arm1.hir_id,
|
arm1.hir_id,
|
||||||
arm1.span,
|
arm_span,
|
||||||
"this match arm has an identical body to the `_` wildcard arm",
|
"this match arm has an identical body to the `_` wildcard arm",
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.span_suggestion(arm1.span, "try removing the arm", "", Applicability::MaybeIncorrect)
|
diag.span_suggestion(arm_span, "try removing the arm", "", appl)
|
||||||
.help("or try changing either arm body")
|
.help("or try changing either arm body")
|
||||||
.span_note(arm2.span, "`_` wildcard arm here");
|
.span_note(arm2.span, "`_` wildcard arm here");
|
||||||
},
|
},
|
||||||
|
@ -144,23 +146,36 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
||||||
keep_arm.span,
|
keep_arm.span,
|
||||||
"this match arm has an identical body to another arm",
|
"this match arm has an identical body to another arm",
|
||||||
|diag| {
|
|diag| {
|
||||||
let move_pat_snip = snippet(cx, move_arm.pat.span, "<pat2>");
|
let move_pat_snip = snippet_with_applicability(cx, move_arm.pat.span, "<pat2>", &mut appl);
|
||||||
let keep_pat_snip = snippet(cx, keep_arm.pat.span, "<pat1>");
|
let keep_pat_snip = snippet_with_applicability(cx, keep_arm.pat.span, "<pat1>", &mut appl);
|
||||||
|
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
keep_arm.pat.span,
|
keep_arm.pat.span,
|
||||||
"try merging the arm patterns",
|
"or try merging the arm patterns",
|
||||||
format!("{keep_pat_snip} | {move_pat_snip}"),
|
format!("{keep_pat_snip} | {move_pat_snip}"),
|
||||||
Applicability::MaybeIncorrect,
|
appl,
|
||||||
)
|
)
|
||||||
.help("or try changing either arm body")
|
.span_suggestion(
|
||||||
.span_note(move_arm.span, "other arm here");
|
adjusted_arm_span(cx, move_arm.span),
|
||||||
|
"and remove this obsolete arm",
|
||||||
|
"",
|
||||||
|
appl,
|
||||||
|
)
|
||||||
|
.help("try changing either arm body");
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extend arm's span to include the comma and whitespaces after it.
|
||||||
|
fn adjusted_arm_span(cx: &LateContext<'_>, span: Span) -> Span {
|
||||||
|
let source_map = cx.sess().source_map();
|
||||||
|
source_map
|
||||||
|
.span_extend_while(span, |c| c == ',' || c.is_ascii_whitespace())
|
||||||
|
.unwrap_or(span)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum NormalizedPat<'a> {
|
enum NormalizedPat<'a> {
|
||||||
Wild,
|
Wild,
|
||||||
|
|
|
@ -115,45 +115,60 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
|
fn is_sig_drop_expr(&mut self, ex: &'tcx Expr<'_>) -> bool {
|
||||||
self.cx.typeck_results().expr_ty(ex)
|
!ex.is_syntactic_place_expr() && self.has_sig_drop_attr(self.cx.typeck_results().expr_ty(ex))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool {
|
fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
|
||||||
!self.seen_types.insert(ty)
|
self.seen_types.clear();
|
||||||
|
self.has_sig_drop_attr_impl(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
fn has_sig_drop_attr_impl(&mut self, ty: Ty<'tcx>) -> bool {
|
||||||
if let Some(adt) = ty.ty_adt_def() {
|
if let Some(adt) = ty.ty_adt_def() {
|
||||||
if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 {
|
if get_attr(
|
||||||
|
self.cx.sess(),
|
||||||
|
self.cx.tcx.get_attrs_unchecked(adt.did()),
|
||||||
|
"has_significant_drop",
|
||||||
|
)
|
||||||
|
.count()
|
||||||
|
> 0
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match ty.kind() {
|
if !self.seen_types.insert(ty) {
|
||||||
rustc_middle::ty::Adt(a, b) => {
|
return false;
|
||||||
for f in a.all_fields() {
|
|
||||||
let ty = f.ty(cx.tcx, b);
|
|
||||||
if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for generic_arg in *b {
|
|
||||||
if let GenericArgKind::Type(ty) = generic_arg.unpack() {
|
|
||||||
if self.has_sig_drop_attr(cx, ty) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
},
|
|
||||||
rustc_middle::ty::Array(ty, _)
|
|
||||||
| rustc_middle::ty::RawPtr(ty, _)
|
|
||||||
| rustc_middle::ty::Ref(_, ty, _)
|
|
||||||
| rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty),
|
|
||||||
_ => false,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let result = match ty.kind() {
|
||||||
|
rustc_middle::ty::Adt(adt, args) => {
|
||||||
|
// if some field has significant drop,
|
||||||
|
adt.all_fields()
|
||||||
|
.map(|field| field.ty(self.cx.tcx, args))
|
||||||
|
.any(|ty| self.has_sig_drop_attr_impl(ty))
|
||||||
|
// or if there is no generic lifetime and..
|
||||||
|
// (to avoid false positive on `Ref<'a, MutexGuard<Foo>>`)
|
||||||
|
|| (args
|
||||||
|
.iter()
|
||||||
|
.all(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
|
||||||
|
// some generic parameter has significant drop
|
||||||
|
// (to avoid false negative on `Box<MutexGuard<Foo>>`)
|
||||||
|
&& args
|
||||||
|
.iter()
|
||||||
|
.filter_map(|arg| match arg.unpack() {
|
||||||
|
GenericArgKind::Type(ty) => Some(ty),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.any(|ty| self.has_sig_drop_attr_impl(ty)))
|
||||||
|
},
|
||||||
|
rustc_middle::ty::Tuple(tys) => tys.iter().any(|ty| self.has_sig_drop_attr_impl(ty)),
|
||||||
|
rustc_middle::ty::Array(ty, _) | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr_impl(*ty),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +247,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
|
||||||
if self.current_sig_drop.is_some() {
|
if self.current_sig_drop.is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let ty = self.sig_drop_checker.get_type(expr);
|
let ty = self.cx.typeck_results().expr_ty(expr);
|
||||||
if ty.is_ref() {
|
if ty.is_ref() {
|
||||||
// We checked that the type was ref, so builtin_deref will return Some,
|
// We checked that the type was ref, so builtin_deref will return Some,
|
||||||
// but let's avoid any chance of an ICE.
|
// but let's avoid any chance of an ICE.
|
||||||
|
@ -279,11 +294,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
|
||||||
|
|
||||||
impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
|
impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
|
||||||
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
|
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
|
||||||
if !self.is_chain_end
|
if !self.is_chain_end && self.sig_drop_checker.is_sig_drop_expr(ex) {
|
||||||
&& self
|
|
||||||
.sig_drop_checker
|
|
||||||
.has_sig_drop_attr(self.cx, self.sig_drop_checker.get_type(ex))
|
|
||||||
{
|
|
||||||
self.has_significant_drop = true;
|
self.has_significant_drop = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -387,10 +398,7 @@ fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'
|
||||||
|
|
||||||
impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> {
|
impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> {
|
||||||
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
||||||
if self
|
if self.sig_drop_checker.is_sig_drop_expr(ex) {
|
||||||
.sig_drop_checker
|
|
||||||
.has_sig_drop_attr(self.sig_drop_checker.cx, self.sig_drop_checker.get_type(ex))
|
|
||||||
{
|
|
||||||
self.found_sig_drop_spans.insert(ex.span);
|
self.found_sig_drop_spans.insert(ex.span);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::macros::{find_format_args, format_args_inputs_span, root_macro_call_first_node};
|
use clippy_utils::macros::{format_args_inputs_span, root_macro_call_first_node, FormatArgsStorage};
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -16,6 +16,7 @@ use super::EXPECT_FUN_CALL;
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
pub(super) fn check<'tcx>(
|
pub(super) fn check<'tcx>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
|
format_args_storage: &FormatArgsStorage,
|
||||||
expr: &hir::Expr<'_>,
|
expr: &hir::Expr<'_>,
|
||||||
method_span: Span,
|
method_span: Span,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
@ -134,9 +135,9 @@ pub(super) fn check<'tcx>(
|
||||||
// Special handling for `format!` as arg_root
|
// Special handling for `format!` as arg_root
|
||||||
if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) {
|
if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) {
|
||||||
if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id)
|
if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id)
|
||||||
&& let Some(format_args) = find_format_args(cx, arg_root, macro_call.expn)
|
&& let Some(format_args) = format_args_storage.get(cx, arg_root, macro_call.expn)
|
||||||
{
|
{
|
||||||
let span = format_args_inputs_span(&format_args);
|
let span = format_args_inputs_span(format_args);
|
||||||
let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
|
let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -126,15 +126,15 @@ enum FilterType {
|
||||||
///
|
///
|
||||||
/// How this is done:
|
/// How this is done:
|
||||||
/// 1. we know that this is invoked in a method call with `filter` as the method name via `mod.rs`
|
/// 1. we know that this is invoked in a method call with `filter` as the method name via `mod.rs`
|
||||||
/// 2. we check that we are in a trait method. Therefore we are in an
|
/// 2. we check that we are in a trait method. Therefore we are in an `(x as
|
||||||
/// `(x as Iterator).filter({filter_arg})` method call.
|
/// Iterator).filter({filter_arg})` method call.
|
||||||
/// 3. we check that the parent expression is not a map. This is because we don't want to lint
|
/// 3. we check that the parent expression is not a map. This is because we don't want to lint
|
||||||
/// twice, and we already have a specialized lint for that.
|
/// twice, and we already have a specialized lint for that.
|
||||||
/// 4. we check that the span of the filter does not contain a comment.
|
/// 4. we check that the span of the filter does not contain a comment.
|
||||||
/// 5. we get the type of the `Item` in the `Iterator`, and compare against the type of Option and
|
/// 5. we get the type of the `Item` in the `Iterator`, and compare against the type of Option and
|
||||||
/// Result.
|
/// Result.
|
||||||
/// 6. we finally check the contents of the filter argument to see if it is a call to `is_some` or
|
/// 6. we finally check the contents of the filter argument to see if it is a call to `is_some` or
|
||||||
/// `is_ok`.
|
/// `is_ok`.
|
||||||
/// 7. if all of the above are true, then we return the `FilterType`
|
/// 7. if all of the above are true, then we return the `FilterType`
|
||||||
fn expression_type(
|
fn expression_type(
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
|
|
|
@ -12,8 +12,10 @@ use rustc_middle::ty;
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
/// lint use of:
|
/// lint use of:
|
||||||
|
///
|
||||||
/// - `hashmap.iter().map(|(_, v)| v)`
|
/// - `hashmap.iter().map(|(_, v)| v)`
|
||||||
/// - `hashmap.into_iter().map(|(_, v)| v)`
|
/// - `hashmap.into_iter().map(|(_, v)| v)`
|
||||||
|
///
|
||||||
/// on `HashMaps` and `BTreeMaps` in std
|
/// on `HashMaps` and `BTreeMaps` in std
|
||||||
|
|
||||||
pub(super) fn check<'tcx>(
|
pub(super) fn check<'tcx>(
|
||||||
|
|
|
@ -69,12 +69,9 @@ pub(super) fn check<'tcx>(
|
||||||
used_move: HirIdSet::default(),
|
used_move: HirIdSet::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
ExprUseVisitor::for_clippy(
|
ExprUseVisitor::for_clippy(cx, closure.def_id, &mut delegate)
|
||||||
cx,
|
.consume_body(body)
|
||||||
closure.def_id,
|
.into_ok();
|
||||||
&mut delegate,
|
|
||||||
)
|
|
||||||
.consume_body(body).into_ok();
|
|
||||||
|
|
||||||
let mut to_be_discarded = false;
|
let mut to_be_discarded = false;
|
||||||
|
|
||||||
|
|
|
@ -133,6 +133,7 @@ use bind_instead_of_map::BindInsteadOfMap;
|
||||||
use clippy_config::msrvs::{self, Msrv};
|
use clippy_config::msrvs::{self, Msrv};
|
||||||
use clippy_utils::consts::{constant, Constant};
|
use clippy_utils::consts::{constant, Constant};
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||||
|
use clippy_utils::macros::FormatArgsStorage;
|
||||||
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
|
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
|
||||||
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty};
|
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty};
|
||||||
pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES;
|
pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES;
|
||||||
|
@ -4087,12 +4088,14 @@ declare_clippy_lint! {
|
||||||
suspicious,
|
suspicious,
|
||||||
"is_empty() called on strings known at compile time"
|
"is_empty() called on strings known at compile time"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Methods {
|
pub struct Methods {
|
||||||
avoid_breaking_exported_api: bool,
|
avoid_breaking_exported_api: bool,
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
allow_expect_in_tests: bool,
|
allow_expect_in_tests: bool,
|
||||||
allow_unwrap_in_tests: bool,
|
allow_unwrap_in_tests: bool,
|
||||||
allowed_dotfiles: FxHashSet<String>,
|
allowed_dotfiles: FxHashSet<String>,
|
||||||
|
format_args: FormatArgsStorage,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Methods {
|
impl Methods {
|
||||||
|
@ -4103,6 +4106,7 @@ impl Methods {
|
||||||
allow_expect_in_tests: bool,
|
allow_expect_in_tests: bool,
|
||||||
allow_unwrap_in_tests: bool,
|
allow_unwrap_in_tests: bool,
|
||||||
mut allowed_dotfiles: FxHashSet<String>,
|
mut allowed_dotfiles: FxHashSet<String>,
|
||||||
|
format_args: FormatArgsStorage,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES.iter().map(ToString::to_string));
|
allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES.iter().map(ToString::to_string));
|
||||||
|
|
||||||
|
@ -4112,6 +4116,7 @@ impl Methods {
|
||||||
allow_expect_in_tests,
|
allow_expect_in_tests,
|
||||||
allow_unwrap_in_tests,
|
allow_unwrap_in_tests,
|
||||||
allowed_dotfiles,
|
allowed_dotfiles,
|
||||||
|
format_args,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4281,7 +4286,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||||
ExprKind::MethodCall(method_call, receiver, args, _) => {
|
ExprKind::MethodCall(method_call, receiver, args, _) => {
|
||||||
let method_span = method_call.ident.span;
|
let method_span = method_call.ident.span;
|
||||||
or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
|
or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
|
||||||
expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
|
expect_fun_call::check(
|
||||||
|
cx,
|
||||||
|
&self.format_args,
|
||||||
|
expr,
|
||||||
|
method_span,
|
||||||
|
method_call.ident.as_str(),
|
||||||
|
receiver,
|
||||||
|
args,
|
||||||
|
);
|
||||||
clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args);
|
clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args);
|
||||||
clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args);
|
clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args);
|
||||||
inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
|
inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
|
||||||
|
|
|
@ -3,10 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::higher::ForLoop;
|
use clippy_utils::higher::ForLoop;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
|
use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
|
||||||
use clippy_utils::{fn_def_id, get_parent_expr};
|
use clippy_utils::visitors::for_each_expr;
|
||||||
|
use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{Expr, ExprKind};
|
use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_span::{sym, Symbol};
|
use rustc_span::{sym, Symbol};
|
||||||
|
|
||||||
|
@ -40,6 +42,53 @@ pub fn check_for_loop_iter(
|
||||||
&& !clone_or_copy_needed
|
&& !clone_or_copy_needed
|
||||||
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
|
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
|
||||||
{
|
{
|
||||||
|
// Issue 12098
|
||||||
|
// https://github.com/rust-lang/rust-clippy/issues/12098
|
||||||
|
// if the assignee have `mut borrow` conflict with the iteratee
|
||||||
|
// the lint should not execute, former didn't consider the mut case
|
||||||
|
|
||||||
|
// check whether `expr` is mutable
|
||||||
|
fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
|
if let Some(hir_id) = path_to_local(expr)
|
||||||
|
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
|
||||||
|
{
|
||||||
|
matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool {
|
||||||
|
let mut change = false;
|
||||||
|
if let ExprKind::Block(block, ..) = body.kind {
|
||||||
|
for_each_expr(block, |e| {
|
||||||
|
match e.kind {
|
||||||
|
ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => {
|
||||||
|
change |= !can_mut_borrow_both(cx, caller, assignee);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
// the return value has no effect but the function need one return value
|
||||||
|
ControlFlow::<()>::Continue(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
change
|
||||||
|
}
|
||||||
|
|
||||||
|
if let ExprKind::Call(_, [child, ..]) = expr.kind {
|
||||||
|
// filter first layer of iterator
|
||||||
|
let mut child = child;
|
||||||
|
// get inner real caller requests for clone
|
||||||
|
while let ExprKind::MethodCall(_, caller, _, _) = child.kind {
|
||||||
|
child = caller;
|
||||||
|
}
|
||||||
|
if is_mutable(cx, child) && is_caller_or_fields_change(cx, body, child) {
|
||||||
|
// skip lint
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// the lint should not be executed if no violation happens
|
||||||
let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind
|
let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind
|
||||||
&& maybe_iter_method_name.ident.name == sym::iter
|
&& maybe_iter_method_name.ident.name == sym::iter
|
||||||
&& let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
|
&& let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
|
||||||
|
|
|
@ -5,7 +5,8 @@ use clippy_utils::ty::implements_trait;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty::{self, GenericArgKind};
|
use rustc_middle::ty;
|
||||||
|
use rustc_middle::ty::GenericArgKind;
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
|
@ -120,6 +120,7 @@ fn pat_bindings(pat: &Pat<'_>) -> Vec<HirId> {
|
||||||
/// operations performed on `binding_hir_ids` are:
|
/// operations performed on `binding_hir_ids` are:
|
||||||
/// * to take non-mutable references to them
|
/// * to take non-mutable references to them
|
||||||
/// * to use them as non-mutable `&self` in method calls
|
/// * to use them as non-mutable `&self` in method calls
|
||||||
|
///
|
||||||
/// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true
|
/// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true
|
||||||
/// when `CloneOrCopyVisitor` is done visiting.
|
/// when `CloneOrCopyVisitor` is done visiting.
|
||||||
struct CloneOrCopyVisitor<'cx, 'tcx> {
|
struct CloneOrCopyVisitor<'cx, 'tcx> {
|
||||||
|
|
|
@ -96,10 +96,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
|
||||||
self.found = true;
|
self.found = true;
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
ExprKind::If(..) => {
|
|
||||||
self.found = true;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
ExprKind::Path(_) => {
|
ExprKind::Path(_) => {
|
||||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||||
if adj
|
if adj
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use clippy_config::msrvs::{self, Msrv};
|
use clippy_config::msrvs::{self, Msrv};
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
|
use clippy_utils::mir::PossibleBorrowerMap;
|
||||||
use clippy_utils::source::snippet_with_context;
|
use clippy_utils::source::snippet_with_context;
|
||||||
use clippy_utils::ty::{implements_trait, is_copy};
|
use clippy_utils::ty::{implements_trait, is_copy};
|
||||||
use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode};
|
use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode};
|
||||||
|
@ -11,7 +11,6 @@ use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath};
|
||||||
use rustc_index::bit_set::BitSet;
|
use rustc_index::bit_set::BitSet;
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::mir::{Rvalue, StatementKind};
|
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty,
|
self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty,
|
||||||
};
|
};
|
||||||
|
@ -106,7 +105,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
|
||||||
}
|
}
|
||||||
&& let count = needless_borrow_count(
|
&& let count = needless_borrow_count(
|
||||||
cx,
|
cx,
|
||||||
&mut self.possible_borrowers,
|
|
||||||
fn_id,
|
fn_id,
|
||||||
cx.typeck_results().node_args(hir_id),
|
cx.typeck_results().node_args(hir_id),
|
||||||
i,
|
i,
|
||||||
|
@ -155,11 +153,9 @@ fn path_has_args(p: &QPath<'_>) -> bool {
|
||||||
/// The following constraints will be checked:
|
/// The following constraints will be checked:
|
||||||
/// * The borrowed expression meets all the generic type's constraints.
|
/// * The borrowed expression meets all the generic type's constraints.
|
||||||
/// * The generic type appears only once in the functions signature.
|
/// * The generic type appears only once in the functions signature.
|
||||||
/// * The borrowed value will not be moved if it is used later in the function.
|
/// * The borrowed value is Copy itself OR not a variable (created by a function call)
|
||||||
#[expect(clippy::too_many_arguments)]
|
|
||||||
fn needless_borrow_count<'tcx>(
|
fn needless_borrow_count<'tcx>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
|
||||||
fn_id: DefId,
|
fn_id: DefId,
|
||||||
callee_args: ty::GenericArgsRef<'tcx>,
|
callee_args: ty::GenericArgsRef<'tcx>,
|
||||||
arg_index: usize,
|
arg_index: usize,
|
||||||
|
@ -234,9 +230,9 @@ fn needless_borrow_count<'tcx>(
|
||||||
|
|
||||||
let referent_ty = cx.typeck_results().expr_ty(referent);
|
let referent_ty = cx.typeck_results().expr_ty(referent);
|
||||||
|
|
||||||
if !is_copy(cx, referent_ty)
|
if (!is_copy(cx, referent_ty) && !referent_ty.is_ref())
|
||||||
&& (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
|
&& let ExprKind::AddrOf(_, _, inner) = reference.kind
|
||||||
|| !referent_used_exactly_once(cx, possible_borrowers, reference))
|
&& !matches!(inner.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -339,37 +335,6 @@ fn is_mixed_projection_predicate<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn referent_used_exactly_once<'tcx>(
|
|
||||||
cx: &LateContext<'tcx>,
|
|
||||||
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
|
||||||
reference: &Expr<'tcx>,
|
|
||||||
) -> bool {
|
|
||||||
if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id)
|
|
||||||
&& let Some(local) = expr_local(cx.tcx, reference)
|
|
||||||
&& let [location] = *local_assignments(mir, local).as_slice()
|
|
||||||
&& let block_data = &mir.basic_blocks[location.block]
|
|
||||||
&& let Some(statement) = block_data.statements.get(location.statement_index)
|
|
||||||
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
|
|
||||||
&& !place.is_indirect_first_projection()
|
|
||||||
{
|
|
||||||
let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
|
|
||||||
if possible_borrowers
|
|
||||||
.last()
|
|
||||||
.map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
|
|
||||||
{
|
|
||||||
possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
|
|
||||||
}
|
|
||||||
let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
|
|
||||||
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
|
|
||||||
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
|
|
||||||
// itself. See the comment in that method for an explanation as to why.
|
|
||||||
possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
|
|
||||||
&& used_exactly_once(mir, place.local).unwrap_or(false)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
|
// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
|
||||||
// projected type that is a type parameter. Returns `false` if replacing the types would have an
|
// projected type that is a type parameter. Returns `false` if replacing the types would have an
|
||||||
// effect on the function signature beyond substituting `new_ty` for `param_ty`.
|
// effect on the function signature beyond substituting `new_ty` for `param_ty`.
|
||||||
|
@ -408,7 +373,11 @@ fn replace_types<'tcx>(
|
||||||
&& let Some(term_ty) = projection_predicate.term.ty()
|
&& let Some(term_ty) = projection_predicate.term.ty()
|
||||||
&& let ty::Param(term_param_ty) = term_ty.kind()
|
&& let ty::Param(term_param_ty) = term_ty.kind()
|
||||||
{
|
{
|
||||||
let projection = projection_predicate.projection_term.with_self_ty(cx.tcx, new_ty).expect_ty(cx.tcx).to_ty(cx.tcx);
|
let projection = projection_predicate
|
||||||
|
.projection_term
|
||||||
|
.with_self_ty(cx.tcx, new_ty)
|
||||||
|
.expect_ty(cx.tcx)
|
||||||
|
.to_ty(cx.tcx);
|
||||||
|
|
||||||
if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
|
if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
|
||||||
&& args[term_param_ty.index as usize] != GenericArg::from(projected_ty)
|
&& args[term_param_ty.index as usize] != GenericArg::from(projected_ty)
|
||||||
|
|
|
@ -178,8 +178,7 @@ impl EarlyLintPass for NeedlessContinue {
|
||||||
/// Given an expression, returns true if either of the following is true
|
/// Given an expression, returns true if either of the following is true
|
||||||
///
|
///
|
||||||
/// - The expression is a `continue` node.
|
/// - The expression is a `continue` node.
|
||||||
/// - The expression node is a block with the first statement being a
|
/// - The expression node is a block with the first statement being a `continue`.
|
||||||
/// `continue`.
|
|
||||||
fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool {
|
fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool {
|
||||||
match else_expr.kind {
|
match else_expr.kind {
|
||||||
ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label),
|
ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label),
|
||||||
|
|
|
@ -273,24 +273,16 @@ fn check<'tcx>(
|
||||||
msg_span,
|
msg_span,
|
||||||
"unneeded late initialization",
|
"unneeded late initialization",
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.tool_only_span_suggestion(
|
diag.multipart_suggestion(
|
||||||
local_stmt.span,
|
format!("move the declaration `{binding_name}` here"),
|
||||||
"remove the local",
|
vec![(local_stmt.span, String::new()), (assign.lhs_span, let_snippet)],
|
||||||
"",
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
|
|
||||||
diag.span_suggestion(
|
|
||||||
assign.lhs_span,
|
|
||||||
format!("declare `{binding_name}` here"),
|
|
||||||
let_snippet,
|
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => {
|
ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => {
|
||||||
let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
|
let (applicability, mut suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
|
||||||
|
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
|
@ -298,30 +290,26 @@ fn check<'tcx>(
|
||||||
local_stmt.span,
|
local_stmt.span,
|
||||||
"unneeded late initialization",
|
"unneeded late initialization",
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
|
suggestions.push((local_stmt.span, String::new()));
|
||||||
|
suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = ")));
|
||||||
diag.span_suggestion_verbose(
|
|
||||||
usage.stmt.span.shrink_to_lo(),
|
|
||||||
format!("declare `{binding_name}` here"),
|
|
||||||
format!("{let_snippet} = "),
|
|
||||||
applicability,
|
|
||||||
);
|
|
||||||
|
|
||||||
diag.multipart_suggestion("remove the assignments from the branches", suggestions, applicability);
|
|
||||||
|
|
||||||
if usage.needs_semi {
|
if usage.needs_semi {
|
||||||
diag.span_suggestion(
|
suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned()));
|
||||||
usage.stmt.span.shrink_to_hi(),
|
|
||||||
"add a semicolon after the `if` expression",
|
|
||||||
";",
|
|
||||||
applicability,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diag.multipart_suggestion(
|
||||||
|
format!(
|
||||||
|
"move the declaration `{binding_name}` here and remove the assignments from the branches"
|
||||||
|
),
|
||||||
|
suggestions,
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
ExprKind::Match(_, arms, MatchSource::Normal) => {
|
ExprKind::Match(_, arms, MatchSource::Normal) => {
|
||||||
let (applicability, suggestions) = assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?;
|
let (applicability, mut suggestions) =
|
||||||
|
assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?;
|
||||||
|
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
|
@ -329,29 +317,18 @@ fn check<'tcx>(
|
||||||
local_stmt.span,
|
local_stmt.span,
|
||||||
"unneeded late initialization",
|
"unneeded late initialization",
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
|
suggestions.push((local_stmt.span, String::new()));
|
||||||
|
suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = ")));
|
||||||
|
|
||||||
diag.span_suggestion_verbose(
|
if usage.needs_semi {
|
||||||
usage.stmt.span.shrink_to_lo(),
|
suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned()));
|
||||||
format!("declare `{binding_name}` here"),
|
}
|
||||||
format!("{let_snippet} = "),
|
|
||||||
applicability,
|
|
||||||
);
|
|
||||||
|
|
||||||
diag.multipart_suggestion(
|
diag.multipart_suggestion(
|
||||||
"remove the assignments from the `match` arms",
|
format!("move the declaration `{binding_name}` here and remove the assignments from the `match` arms"),
|
||||||
suggestions,
|
suggestions,
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
|
|
||||||
if usage.needs_semi {
|
|
||||||
diag.span_suggestion(
|
|
||||||
usage.stmt.span.shrink_to_hi(),
|
|
||||||
"add a semicolon after the `match` expression",
|
|
||||||
";",
|
|
||||||
applicability,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -117,7 +117,9 @@ fn check_closures<'tcx>(
|
||||||
.associated_body()
|
.associated_body()
|
||||||
.map(|(_, body_id)| hir.body(body_id))
|
.map(|(_, body_id)| hir.body(body_id))
|
||||||
{
|
{
|
||||||
euv::ExprUseVisitor::for_clippy(cx, closure, &mut *ctx).consume_body(body).into_ok();
|
euv::ExprUseVisitor::for_clippy(cx, closure, &mut *ctx)
|
||||||
|
.consume_body(body)
|
||||||
|
.into_ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,7 +196,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
||||||
async_closures: FxHashSet::default(),
|
async_closures: FxHashSet::default(),
|
||||||
tcx: cx.tcx,
|
tcx: cx.tcx,
|
||||||
};
|
};
|
||||||
euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body).into_ok();
|
euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx)
|
||||||
|
.consume_body(body)
|
||||||
|
.into_ok();
|
||||||
|
|
||||||
let mut checked_closures = FxHashSet::default();
|
let mut checked_closures = FxHashSet::default();
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
||||||
// function body.
|
// function body.
|
||||||
let MovedVariablesCtxt { moved_vars } = {
|
let MovedVariablesCtxt { moved_vars } = {
|
||||||
let mut ctx = MovedVariablesCtxt::default();
|
let mut ctx = MovedVariablesCtxt::default();
|
||||||
euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body).into_ok();
|
euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx)
|
||||||
|
.consume_body(body)
|
||||||
|
.into_ok();
|
||||||
ctx
|
ctx
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,6 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
|
||||||
|
|
||||||
fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) {
|
fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) {
|
||||||
for hir_id in self.local_bindings.pop().unwrap() {
|
for hir_id in self.local_bindings.pop().unwrap() {
|
||||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
|
||||||
if let Some(span) = self.underscore_bindings.swap_remove(&hir_id) {
|
if let Some(span) = self.underscore_bindings.swap_remove(&hir_id) {
|
||||||
span_lint_hir(
|
span_lint_hir(
|
||||||
cx,
|
cx,
|
||||||
|
@ -109,7 +108,6 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
|
||||||
|
|
||||||
fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||||
if let Some(def_id) = path_to_local(expr) {
|
if let Some(def_id) = path_to_local(expr) {
|
||||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
|
||||||
self.underscore_bindings.swap_remove(&def_id);
|
self.underscore_bindings.swap_remove(&def_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,7 +116,11 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
|
||||||
impl NoEffect {
|
impl NoEffect {
|
||||||
fn check_no_effect(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
fn check_no_effect(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||||
if let StmtKind::Semi(expr) = stmt.kind {
|
if let StmtKind::Semi(expr) = stmt.kind {
|
||||||
// move `expr.span.from_expansion()` ahead
|
// Covered by rustc `path_statements` lint
|
||||||
|
if matches!(expr.kind, ExprKind::Path(_)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if expr.span.from_expansion() {
|
if expr.span.from_expansion() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -389,6 +389,10 @@ declare_lint_pass!(StrToString => [STR_TO_STRING]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for StrToString {
|
impl<'tcx> LateLintPass<'tcx> for StrToString {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||||
|
if expr.span.from_expansion() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
|
if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
|
||||||
&& path.ident.name == sym::to_string
|
&& path.ident.name == sym::to_string
|
||||||
&& let ty = cx.typeck_results().expr_ty(self_arg)
|
&& let ty = cx.typeck_results().expr_ty(self_arg)
|
||||||
|
@ -437,6 +441,10 @@ declare_lint_pass!(StringToString => [STRING_TO_STRING]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for StringToString {
|
impl<'tcx> LateLintPass<'tcx> for StringToString {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||||
|
if expr.span.from_expansion() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
|
if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
|
||||||
&& path.ident.name == sym::to_string
|
&& path.ident.name == sym::to_string
|
||||||
&& let ty = cx.typeck_results().expr_ty(self_arg)
|
&& let ty = cx.typeck_results().expr_ty(self_arg)
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub(super) fn check<'tcx>(
|
||||||
|
|
||||||
let int_ty = substs.type_at(0);
|
let int_ty = substs.type_at(0);
|
||||||
if from_ty != int_ty {
|
if from_ty != int_ty {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
|
|
|
@ -251,11 +251,7 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
|
||||||
local_id: unwrap_info.local_id,
|
local_id: unwrap_info.local_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let vis = ExprUseVisitor::for_clippy(
|
let vis = ExprUseVisitor::for_clippy(self.cx, cond.hir_id.owner.def_id, &mut delegate);
|
||||||
self.cx,
|
|
||||||
cond.hir_id.owner.def_id,
|
|
||||||
&mut delegate,
|
|
||||||
);
|
|
||||||
vis.walk_expr(cond).into_ok();
|
vis.walk_expr(cond).into_ok();
|
||||||
vis.walk_expr(branch).into_ok();
|
vis.walk_expr(branch).into_ok();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use clippy_utils::macros::AST_FORMAT_ARGS;
|
use clippy_utils::macros::FormatArgsStorage;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_ast::{Crate, Expr, ExprKind, FormatArgs};
|
use rustc_ast::{Crate, Expr, ExprKind, FormatArgs};
|
||||||
|
@ -9,13 +9,20 @@ use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::{hygiene, Span};
|
use rustc_span::{hygiene, Span};
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
/// Collects [`rustc_ast::FormatArgs`] so that future late passes can call
|
/// Populates [`FormatArgsStorage`] with AST [`FormatArgs`] nodes
|
||||||
/// [`clippy_utils::macros::find_format_args`]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct FormatArgsCollector {
|
pub struct FormatArgsCollector {
|
||||||
format_args: FxHashMap<Span, Rc<FormatArgs>>,
|
format_args: FxHashMap<Span, FormatArgs>,
|
||||||
|
storage: FormatArgsStorage,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormatArgsCollector {
|
||||||
|
pub fn new(storage: FormatArgsStorage) -> Self {
|
||||||
|
Self {
|
||||||
|
format_args: FxHashMap::default(),
|
||||||
|
storage,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_lint_pass!(FormatArgsCollector => []);
|
impl_lint_pass!(FormatArgsCollector => []);
|
||||||
|
@ -27,16 +34,12 @@ impl EarlyLintPass for FormatArgsCollector {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.format_args
|
self.format_args.insert(expr.span.with_parent(None), (**args).clone());
|
||||||
.insert(expr.span.with_parent(None), Rc::new((**args).clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) {
|
fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) {
|
||||||
AST_FORMAT_ARGS.with(|ast_format_args| {
|
self.storage.set(mem::take(&mut self.format_args));
|
||||||
let result = ast_format_args.set(mem::take(&mut self.format_args));
|
|
||||||
debug_assert!(result.is_ok());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||||
use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall};
|
use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall};
|
||||||
use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
|
use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
|
||||||
use clippy_utils::{is_in_cfg_test, is_in_test_function};
|
use clippy_utils::{is_in_cfg_test, is_in_test_function};
|
||||||
use rustc_ast::token::LitKind;
|
use rustc_ast::token::LitKind;
|
||||||
|
@ -236,13 +236,15 @@ declare_clippy_lint! {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Write {
|
pub struct Write {
|
||||||
|
format_args: FormatArgsStorage,
|
||||||
in_debug_impl: bool,
|
in_debug_impl: bool,
|
||||||
allow_print_in_tests: bool,
|
allow_print_in_tests: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write {
|
impl Write {
|
||||||
pub fn new(allow_print_in_tests: bool) -> Self {
|
pub fn new(format_args: FormatArgsStorage, allow_print_in_tests: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
format_args,
|
||||||
allow_print_in_tests,
|
allow_print_in_tests,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
|
@ -307,7 +309,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
|
||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(format_args) = find_format_args(cx, expr, macro_call.expn) {
|
if let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) {
|
||||||
// ignore `writeln!(w)` and `write!(v, some_macro!())`
|
// ignore `writeln!(w)` and `write!(v, some_macro!())`
|
||||||
if format_args.span.from_expansion() {
|
if format_args.span.from_expansion() {
|
||||||
return;
|
return;
|
||||||
|
@ -315,15 +317,15 @@ impl<'tcx> LateLintPass<'tcx> for Write {
|
||||||
|
|
||||||
match diag_name {
|
match diag_name {
|
||||||
sym::print_macro | sym::eprint_macro | sym::write_macro => {
|
sym::print_macro | sym::eprint_macro | sym::write_macro => {
|
||||||
check_newline(cx, &format_args, ¯o_call, name);
|
check_newline(cx, format_args, ¯o_call, name);
|
||||||
},
|
},
|
||||||
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
|
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
|
||||||
check_empty_string(cx, &format_args, ¯o_call, name);
|
check_empty_string(cx, format_args, ¯o_call, name);
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
check_literal(cx, &format_args, name);
|
check_literal(cx, format_args, name);
|
||||||
|
|
||||||
if !self.in_debug_impl {
|
if !self.in_debug_impl {
|
||||||
for piece in &format_args.template {
|
for piece in &format_args.template {
|
||||||
|
|
|
@ -193,6 +193,21 @@ pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the given local has an initializer or is from something other than a `let` statement
|
||||||
|
///
|
||||||
|
/// e.g. returns true for `x` in `fn f(x: usize) { .. }` and `let x = 1;` but false for `let x;`
|
||||||
|
pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
|
||||||
|
for (_, node) in cx.tcx.hir().parent_iter(local) {
|
||||||
|
match node {
|
||||||
|
Node::Pat(..) | Node::PatField(..) => {},
|
||||||
|
Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
|
||||||
|
_ => return true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if the given `NodeId` is inside a constant context
|
/// Returns `true` if the given `NodeId` is inside a constant context
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
@ -1499,15 +1514,18 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether the given `Expr` is a range equivalent to a `RangeFull`.
|
/// Checks whether the given `Expr` is a range equivalent to a `RangeFull`.
|
||||||
|
///
|
||||||
/// For the lower bound, this means that:
|
/// For the lower bound, this means that:
|
||||||
/// - either there is none
|
/// - either there is none
|
||||||
/// - or it is the smallest value that can be represented by the range's integer type
|
/// - or it is the smallest value that can be represented by the range's integer type
|
||||||
|
///
|
||||||
/// For the upper bound, this means that:
|
/// For the upper bound, this means that:
|
||||||
/// - either there is none
|
/// - either there is none
|
||||||
/// - or it is the largest value that can be represented by the range's integer type and is
|
/// - or it is the largest value that can be represented by the range's integer type and is
|
||||||
/// inclusive
|
/// inclusive
|
||||||
/// - or it is a call to some container's `len` method and is exclusive, and the range is passed to
|
/// - or it is a call to some container's `len` method and is exclusive, and the range is passed to
|
||||||
/// a method call on that same container (e.g. `v.drain(..v.len())`)
|
/// a method call on that same container (e.g. `v.drain(..v.len())`)
|
||||||
|
///
|
||||||
/// If the given `Expr` is not some kind of range, the function returns `false`.
|
/// If the given `Expr` is not some kind of range, the function returns `false`.
|
||||||
pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
|
pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
|
||||||
let ty = cx.typeck_results().expr_ty(expr);
|
let ty = cx.typeck_results().expr_ty(expr);
|
||||||
|
|
|
@ -5,15 +5,13 @@ use crate::visitors::{for_each_expr, Descend};
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
|
use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
|
use rustc_data_structures::sync::{Lrc, OnceLock};
|
||||||
use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
|
use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
|
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
|
||||||
use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
|
use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
|
||||||
use std::cell::OnceCell;
|
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
|
|
||||||
const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
|
const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
|
||||||
sym::assert_eq_macro,
|
sym::assert_eq_macro,
|
||||||
|
@ -388,50 +386,44 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local! {
|
/// Stores AST [`FormatArgs`] nodes for use in late lint passes, as they are in a desugared form in
|
||||||
/// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be
|
/// the HIR
|
||||||
/// able to access the many features of a [`LateContext`].
|
#[derive(Default, Clone)]
|
||||||
|
pub struct FormatArgsStorage(Lrc<OnceLock<FxHashMap<Span, FormatArgs>>>);
|
||||||
|
|
||||||
|
impl FormatArgsStorage {
|
||||||
|
/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of
|
||||||
|
/// `expn_id`
|
||||||
///
|
///
|
||||||
/// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
|
/// See also [`find_format_arg_expr`]
|
||||||
/// assumption that the early pass that populates the map and the later late passes will all be
|
pub fn get(&self, cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<&FormatArgs> {
|
||||||
/// running on the same thread.
|
let format_args_expr = for_each_expr(start, |expr| {
|
||||||
#[doc(hidden)]
|
let ctxt = expr.span.ctxt();
|
||||||
pub static AST_FORMAT_ARGS: OnceCell<FxHashMap<Span, Rc<FormatArgs>>> = {
|
if ctxt.outer_expn().is_descendant_of(expn_id) {
|
||||||
static CALLED: AtomicBool = AtomicBool::new(false);
|
if macro_backtrace(expr.span)
|
||||||
debug_assert!(
|
.map(|macro_call| cx.tcx.item_name(macro_call.def_id))
|
||||||
!CALLED.swap(true, Ordering::SeqCst),
|
.any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))
|
||||||
"incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
|
{
|
||||||
);
|
ControlFlow::Break(expr)
|
||||||
|
} else {
|
||||||
OnceCell::new()
|
ControlFlow::Continue(Descend::Yes)
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of
|
|
||||||
/// `expn_id`
|
|
||||||
pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<Rc<FormatArgs>> {
|
|
||||||
let format_args_expr = for_each_expr(start, |expr| {
|
|
||||||
let ctxt = expr.span.ctxt();
|
|
||||||
if ctxt.outer_expn().is_descendant_of(expn_id) {
|
|
||||||
if macro_backtrace(expr.span)
|
|
||||||
.map(|macro_call| cx.tcx.item_name(macro_call.def_id))
|
|
||||||
.any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))
|
|
||||||
{
|
|
||||||
ControlFlow::Break(expr)
|
|
||||||
} else {
|
} else {
|
||||||
ControlFlow::Continue(Descend::Yes)
|
ControlFlow::Continue(Descend::No)
|
||||||
}
|
}
|
||||||
} else {
|
})?;
|
||||||
ControlFlow::Continue(Descend::No)
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
AST_FORMAT_ARGS.with(|ast_format_args| {
|
debug_assert!(self.0.get().is_some(), "`FormatArgsStorage` not yet populated");
|
||||||
ast_format_args
|
|
||||||
.get()?
|
self.0.get()?.get(&format_args_expr.span.with_parent(None))
|
||||||
.get(&format_args_expr.span.with_parent(None))
|
}
|
||||||
.cloned()
|
|
||||||
})
|
/// Should only be called by `FormatArgsCollector`
|
||||||
|
pub fn set(&self, format_args: FxHashMap<Span, FormatArgs>) {
|
||||||
|
self.0
|
||||||
|
.set(format_args)
|
||||||
|
.expect("`FormatArgsStorage::set` should only be called once");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
|
/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
|
// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
|
||||||
// differ from the time of `rustc` even if the name stays the same.
|
// differ from the time of `rustc` even if the name stays the same.
|
||||||
|
|
||||||
use clippy_config::msrvs::Msrv;
|
use clippy_config::msrvs::{self, Msrv};
|
||||||
use hir::LangItem;
|
use hir::LangItem;
|
||||||
use rustc_attr::StableSince;
|
use rustc_attr::StableSince;
|
||||||
use rustc_const_eval::transform::check_consts::ConstCx;
|
use rustc_const_eval::transform::check_consts::ConstCx;
|
||||||
|
@ -42,7 +42,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv)
|
||||||
for bb in &*body.basic_blocks {
|
for bb in &*body.basic_blocks {
|
||||||
check_terminator(tcx, body, bb.terminator(), msrv)?;
|
check_terminator(tcx, body, bb.terminator(), msrv)?;
|
||||||
for stmt in &bb.statements {
|
for stmt in &bb.statements {
|
||||||
check_statement(tcx, body, def_id, stmt)?;
|
check_statement(tcx, body, def_id, stmt, msrv)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -102,13 +102,14 @@ fn check_rvalue<'tcx>(
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
rvalue: &Rvalue<'tcx>,
|
rvalue: &Rvalue<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
msrv: &Msrv,
|
||||||
) -> McfResult {
|
) -> McfResult {
|
||||||
match rvalue {
|
match rvalue {
|
||||||
Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
|
Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
|
||||||
Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
|
Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
|
||||||
check_place(tcx, *place, span, body)
|
check_place(tcx, *place, span, body, msrv)
|
||||||
},
|
},
|
||||||
Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body),
|
Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body, msrv),
|
||||||
Rvalue::Repeat(operand, _)
|
Rvalue::Repeat(operand, _)
|
||||||
| Rvalue::Use(operand)
|
| Rvalue::Use(operand)
|
||||||
| Rvalue::Cast(
|
| Rvalue::Cast(
|
||||||
|
@ -122,7 +123,7 @@ fn check_rvalue<'tcx>(
|
||||||
| CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer),
|
| CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer),
|
||||||
operand,
|
operand,
|
||||||
_,
|
_,
|
||||||
) => check_operand(tcx, operand, span, body),
|
) => check_operand(tcx, operand, span, body, msrv),
|
||||||
Rvalue::Cast(
|
Rvalue::Cast(
|
||||||
CastKind::PointerCoercion(
|
CastKind::PointerCoercion(
|
||||||
PointerCoercion::UnsafeFnPointer
|
PointerCoercion::UnsafeFnPointer
|
||||||
|
@ -133,15 +134,13 @@ fn check_rvalue<'tcx>(
|
||||||
_,
|
_,
|
||||||
) => Err((span, "function pointer casts are not allowed in const fn".into())),
|
) => Err((span, "function pointer casts are not allowed in const fn".into())),
|
||||||
Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), op, cast_ty) => {
|
Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), op, cast_ty) => {
|
||||||
let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
|
let Some(pointee_ty) = cast_ty.builtin_deref(true) else {
|
||||||
deref_ty
|
|
||||||
} else {
|
|
||||||
// We cannot allow this for now.
|
// We cannot allow this for now.
|
||||||
return Err((span, "unsizing casts are only allowed for references right now".into()));
|
return Err((span, "unsizing casts are only allowed for references right now".into()));
|
||||||
};
|
};
|
||||||
let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
|
let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
|
||||||
if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
|
if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
|
||||||
check_operand(tcx, op, span, body)?;
|
check_operand(tcx, op, span, body, msrv)?;
|
||||||
// Casting/coercing things to slices is fine.
|
// Casting/coercing things to slices is fine.
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -162,8 +161,8 @@ fn check_rvalue<'tcx>(
|
||||||
)),
|
)),
|
||||||
// binops are fine on integers
|
// binops are fine on integers
|
||||||
Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
|
Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
|
||||||
check_operand(tcx, lhs, span, body)?;
|
check_operand(tcx, lhs, span, body, msrv)?;
|
||||||
check_operand(tcx, rhs, span, body)?;
|
check_operand(tcx, rhs, span, body, msrv)?;
|
||||||
let ty = lhs.ty(body, tcx);
|
let ty = lhs.ty(body, tcx);
|
||||||
if ty.is_integral() || ty.is_bool() || ty.is_char() {
|
if ty.is_integral() || ty.is_bool() || ty.is_char() {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -179,14 +178,14 @@ fn check_rvalue<'tcx>(
|
||||||
Rvalue::UnaryOp(_, operand) => {
|
Rvalue::UnaryOp(_, operand) => {
|
||||||
let ty = operand.ty(body, tcx);
|
let ty = operand.ty(body, tcx);
|
||||||
if ty.is_integral() || ty.is_bool() {
|
if ty.is_integral() || ty.is_bool() {
|
||||||
check_operand(tcx, operand, span, body)
|
check_operand(tcx, operand, span, body, msrv)
|
||||||
} else {
|
} else {
|
||||||
Err((span, "only int and `bool` operations are stable in const fn".into()))
|
Err((span, "only int and `bool` operations are stable in const fn".into()))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Rvalue::Aggregate(_, operands) => {
|
Rvalue::Aggregate(_, operands) => {
|
||||||
for operand in operands {
|
for operand in operands {
|
||||||
check_operand(tcx, operand, span, body)?;
|
check_operand(tcx, operand, span, body, msrv)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
@ -198,28 +197,29 @@ fn check_statement<'tcx>(
|
||||||
body: &Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
statement: &Statement<'tcx>,
|
statement: &Statement<'tcx>,
|
||||||
|
msrv: &Msrv,
|
||||||
) -> McfResult {
|
) -> McfResult {
|
||||||
let span = statement.source_info.span;
|
let span = statement.source_info.span;
|
||||||
match &statement.kind {
|
match &statement.kind {
|
||||||
StatementKind::Assign(box (place, rval)) => {
|
StatementKind::Assign(box (place, rval)) => {
|
||||||
check_place(tcx, *place, span, body)?;
|
check_place(tcx, *place, span, body, msrv)?;
|
||||||
check_rvalue(tcx, body, def_id, rval, span)
|
check_rvalue(tcx, body, def_id, rval, span, msrv)
|
||||||
},
|
},
|
||||||
|
|
||||||
StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
|
StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body, msrv),
|
||||||
// just an assignment
|
// just an assignment
|
||||||
StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
|
StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
|
||||||
check_place(tcx, **place, span, body)
|
check_place(tcx, **place, span, body, msrv)
|
||||||
},
|
},
|
||||||
|
|
||||||
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body),
|
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body, msrv),
|
||||||
|
|
||||||
StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
|
StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
|
||||||
rustc_middle::mir::CopyNonOverlapping { dst, src, count },
|
rustc_middle::mir::CopyNonOverlapping { dst, src, count },
|
||||||
)) => {
|
)) => {
|
||||||
check_operand(tcx, dst, span, body)?;
|
check_operand(tcx, dst, span, body, msrv)?;
|
||||||
check_operand(tcx, src, span, body)?;
|
check_operand(tcx, src, span, body, msrv)?;
|
||||||
check_operand(tcx, count, span, body)
|
check_operand(tcx, count, span, body, msrv)
|
||||||
},
|
},
|
||||||
// These are all NOPs
|
// These are all NOPs
|
||||||
StatementKind::StorageLive(_)
|
StatementKind::StorageLive(_)
|
||||||
|
@ -233,7 +233,13 @@ fn check_statement<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
|
fn check_operand<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
operand: &Operand<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
body: &Body<'tcx>,
|
||||||
|
msrv: &Msrv,
|
||||||
|
) -> McfResult {
|
||||||
match operand {
|
match operand {
|
||||||
Operand::Move(place) => {
|
Operand::Move(place) => {
|
||||||
if !place.projection.as_ref().is_empty()
|
if !place.projection.as_ref().is_empty()
|
||||||
|
@ -245,9 +251,9 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
check_place(tcx, *place, span, body)
|
check_place(tcx, *place, span, body, msrv)
|
||||||
},
|
},
|
||||||
Operand::Copy(place) => check_place(tcx, *place, span, body),
|
Operand::Copy(place) => check_place(tcx, *place, span, body, msrv),
|
||||||
Operand::Constant(c) => match c.check_static_ptr(tcx) {
|
Operand::Constant(c) => match c.check_static_ptr(tcx) {
|
||||||
Some(_) => Err((span, "cannot access `static` items in const fn".into())),
|
Some(_) => Err((span, "cannot access `static` items in const fn".into())),
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
|
@ -255,23 +261,27 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
|
fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>, msrv: &Msrv) -> McfResult {
|
||||||
for (base, elem) in place.as_ref().iter_projections() {
|
for (base, elem) in place.as_ref().iter_projections() {
|
||||||
match elem {
|
match elem {
|
||||||
ProjectionElem::Field(..) => {
|
ProjectionElem::Field(..) => {
|
||||||
let base_ty = base.ty(body, tcx).ty;
|
if base.ty(body, tcx).ty.is_union() && !msrv.meets(msrvs::CONST_FN_UNION) {
|
||||||
if let Some(def) = base_ty.ty_adt_def() {
|
return Err((span, "accessing union fields is unstable".into()));
|
||||||
// No union field accesses in `const fn`
|
|
||||||
if def.is_union() {
|
|
||||||
return Err((span, "accessing union fields is unstable".into()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
ProjectionElem::Deref => match base.ty(body, tcx).ty.kind() {
|
||||||
|
ty::RawPtr(_, hir::Mutability::Mut) => {
|
||||||
|
return Err((span, "dereferencing raw mut pointer in const fn is unstable".into()));
|
||||||
|
},
|
||||||
|
ty::RawPtr(_, hir::Mutability::Not) if !msrv.meets(msrvs::CONST_RAW_PTR_DEREF) => {
|
||||||
|
return Err((span, "dereferencing raw const pointer in const fn is unstable".into()));
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
ProjectionElem::ConstantIndex { .. }
|
ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::OpaqueCast(..)
|
| ProjectionElem::OpaqueCast(..)
|
||||||
| ProjectionElem::Downcast(..)
|
| ProjectionElem::Downcast(..)
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
| ProjectionElem::Deref
|
|
||||||
| ProjectionElem::Subtype(_)
|
| ProjectionElem::Subtype(_)
|
||||||
| ProjectionElem::Index(_) => {},
|
| ProjectionElem::Index(_) => {},
|
||||||
}
|
}
|
||||||
|
@ -304,7 +314,7 @@ fn check_terminator<'tcx>(
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body),
|
TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body, msrv),
|
||||||
TerminatorKind::CoroutineDrop | TerminatorKind::Yield { .. } => {
|
TerminatorKind::CoroutineDrop | TerminatorKind::Yield { .. } => {
|
||||||
Err((span, "const fn coroutines are unstable".into()))
|
Err((span, "const fn coroutines are unstable".into()))
|
||||||
},
|
},
|
||||||
|
@ -341,10 +351,10 @@ fn check_terminator<'tcx>(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
check_operand(tcx, func, span, body)?;
|
check_operand(tcx, func, span, body, msrv)?;
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
check_operand(tcx, &arg.node, span, body)?;
|
check_operand(tcx, &arg.node, span, body, msrv)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -357,7 +367,7 @@ fn check_terminator<'tcx>(
|
||||||
msg: _,
|
msg: _,
|
||||||
target: _,
|
target: _,
|
||||||
unwind: _,
|
unwind: _,
|
||||||
} => check_operand(tcx, cond, span, body),
|
} => check_operand(tcx, cond, span, body, msrv),
|
||||||
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
|
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
|
||||||
use rustc_lint::{LateContext, LintContext};
|
use rustc_lint::{LateContext, LintContext};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::source_map::{original_sp, SourceMap};
|
use rustc_span::source_map::{original_sp, SourceMap};
|
||||||
use rustc_span::{hygiene, BytePos, SourceFileAndLine, Pos, SourceFile, Span, SpanData, SyntaxContext, DUMMY_SP};
|
use rustc_span::{hygiene, BytePos, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, DUMMY_SP};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<
|
||||||
/// - Applicability level `Unspecified` will never be changed.
|
/// - Applicability level `Unspecified` will never be changed.
|
||||||
/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
|
/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
|
||||||
/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
|
/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
|
||||||
/// `HasPlaceholders`
|
/// `HasPlaceholders`
|
||||||
pub fn snippet_with_applicability<'a, T: LintContext>(
|
pub fn snippet_with_applicability<'a, T: LintContext>(
|
||||||
cx: &T,
|
cx: &T,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
|
@ -67,8 +67,7 @@ impl<'a> Sugg<'a> {
|
||||||
/// - Applicability level `Unspecified` will never be changed.
|
/// - Applicability level `Unspecified` will never be changed.
|
||||||
/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
|
/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
|
||||||
/// - If the default value is used and the applicability level is `MachineApplicable`, change it
|
/// - If the default value is used and the applicability level is `MachineApplicable`, change it
|
||||||
/// to
|
/// to `HasPlaceholders`
|
||||||
/// `HasPlaceholders`
|
|
||||||
pub fn hir_with_applicability(
|
pub fn hir_with_applicability(
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
expr: &hir::Expr<'_>,
|
expr: &hir::Expr<'_>,
|
||||||
|
|
|
@ -18,8 +18,8 @@ use rustc_middle::traits::EvaluationResult;
|
||||||
use rustc_middle::ty::layout::ValidityRequirement;
|
use rustc_middle::ty::layout::ValidityRequirement;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
|
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
|
||||||
GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, ToPredicate, TraitRef, Ty, TyCtxt,
|
GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, ToPredicate, TraitRef, Ty, TyCtxt, TypeSuperVisitable,
|
||||||
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
|
TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
|
||||||
};
|
};
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
||||||
|
@ -273,11 +273,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
|
||||||
let infcx = tcx.infer_ctxt().build();
|
let infcx = tcx.infer_ctxt().build();
|
||||||
let args = args
|
let args = args
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|arg| {
|
.map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into()))
|
||||||
arg.into().unwrap_or_else(|| {
|
|
||||||
infcx.next_ty_var(DUMMY_SP).into()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// If an effect arg was not specified, we need to specify it.
|
// If an effect arg was not specified, we need to specify it.
|
||||||
|
@ -795,7 +791,8 @@ fn sig_from_bounds<'tcx>(
|
||||||
inputs = Some(i);
|
inputs = Some(i);
|
||||||
},
|
},
|
||||||
ty::ClauseKind::Projection(p)
|
ty::ClauseKind::Projection(p)
|
||||||
if Some(p.projection_term.def_id) == lang_items.fn_once_output() && p.projection_term.self_ty() == ty =>
|
if Some(p.projection_term.def_id) == lang_items.fn_once_output()
|
||||||
|
&& p.projection_term.self_ty() == ty =>
|
||||||
{
|
{
|
||||||
if output.is_some() {
|
if output.is_some() {
|
||||||
// Multiple different fn trait impls. Is this even allowed?
|
// Multiple different fn trait impls. Is this even allowed?
|
||||||
|
@ -956,11 +953,7 @@ pub struct AdtVariantInfo {
|
||||||
|
|
||||||
impl AdtVariantInfo {
|
impl AdtVariantInfo {
|
||||||
/// Returns ADT variants ordered by size
|
/// Returns ADT variants ordered by size
|
||||||
pub fn new<'tcx>(
|
pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: GenericArgsRef<'tcx>) -> Vec<Self> {
|
||||||
cx: &LateContext<'tcx>,
|
|
||||||
adt: AdtDef<'tcx>,
|
|
||||||
subst: GenericArgsRef<'tcx>
|
|
||||||
) -> Vec<Self> {
|
|
||||||
let mut variants_size = adt
|
let mut variants_size = adt
|
||||||
.variants()
|
.variants()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -16,13 +16,9 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) ->
|
||||||
used_mutably: HirIdSet::default(),
|
used_mutably: HirIdSet::default(),
|
||||||
skip: false,
|
skip: false,
|
||||||
};
|
};
|
||||||
ExprUseVisitor::for_clippy(
|
ExprUseVisitor::for_clippy(cx, expr.hir_id.owner.def_id, &mut delegate)
|
||||||
cx,
|
.walk_expr(expr)
|
||||||
expr.hir_id.owner.def_id,
|
.into_ok();
|
||||||
&mut delegate,
|
|
||||||
)
|
|
||||||
.walk_expr(expr)
|
|
||||||
.into_ok();
|
|
||||||
|
|
||||||
if delegate.skip {
|
if delegate.skip {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -13,7 +13,7 @@ default-run = "lintcheck"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.69"
|
anyhow = "1.0.69"
|
||||||
cargo_metadata = "0.15.3"
|
cargo_metadata = "0.15.3"
|
||||||
clap = { version = "4.1.8", features = ["derive", "env"] }
|
clap = { version = "4.4", features = ["derive", "env"] }
|
||||||
crates_io_api = "0.8.1"
|
crates_io_api = "0.8.1"
|
||||||
crossbeam-channel = "0.5.6"
|
crossbeam-channel = "0.5.6"
|
||||||
flate2 = "1.0"
|
flate2 = "1.0"
|
||||||
|
|
|
@ -42,4 +42,32 @@ help: to have lints override the group set `pedantic` to a lower priority
|
||||||
19 | pedantic = { level = "warn", priority = -2 }
|
19 | pedantic = { level = "warn", priority = -2 }
|
||||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
error: could not compile `fail` (lib) due to 3 previous errors
|
error: lint group `rust_2018_idioms` has the same priority (0) as a lint
|
||||||
|
--> Cargo.toml:23:1
|
||||||
|
|
|
||||||
|
23 | rust_2018_idioms = "warn"
|
||||||
|
| ^^^^^^^^^^^^^^^^ ------ has an implicit priority of 0
|
||||||
|
24 | bare_trait_objects = "allow"
|
||||||
|
| ------------------ has the same priority as this lint
|
||||||
|
|
|
||||||
|
= note: the order of the lints in the table is ignored by Cargo
|
||||||
|
help: to have lints override the group set `rust_2018_idioms` to a lower priority
|
||||||
|
|
|
||||||
|
23 | rust_2018_idioms = { level = "warn", priority = -1 }
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: lint group `pedantic` has the same priority (0) as a lint
|
||||||
|
--> Cargo.toml:27:1
|
||||||
|
|
|
||||||
|
27 | pedantic = "warn"
|
||||||
|
| ^^^^^^^^ ------ has an implicit priority of 0
|
||||||
|
28 | similar_names = "allow"
|
||||||
|
| ------------- has the same priority as this lint
|
||||||
|
|
|
||||||
|
= note: the order of the lints in the table is ignored by Cargo
|
||||||
|
help: to have lints override the group set `pedantic` to a lower priority
|
||||||
|
|
|
||||||
|
27 | pedantic = { level = "warn", priority = -1 }
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: could not compile `fail` (lib) due to 5 previous errors
|
||||||
|
|
|
@ -18,3 +18,11 @@ deprecated = "allow"
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
pedantic = { level = "warn", priority = -1 }
|
pedantic = { level = "warn", priority = -1 }
|
||||||
similar_names = { level = "allow", priority = -1 }
|
similar_names = { level = "allow", priority = -1 }
|
||||||
|
|
||||||
|
[workspace.lints.rust]
|
||||||
|
rust_2018_idioms = "warn"
|
||||||
|
bare_trait_objects = "allow"
|
||||||
|
|
||||||
|
[workspace.lints.clippy]
|
||||||
|
pedantic = "warn"
|
||||||
|
similar_names = "allow"
|
||||||
|
|
|
@ -19,8 +19,8 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]
|
||||||
error: hardcoded path to a diagnostic item
|
error: hardcoded path to a diagnostic item
|
||||||
--> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:43
|
--> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:43
|
||||||
|
|
|
|
||||||
LL | const OPS_MOD: [&str; 5] = ["core", "ops"];
|
LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: convert all references to use `sym::deref_method`
|
= help: convert all references to use `sym::deref_method`
|
||||||
|
|
||||||
|
|
260
tests/ui-toml/macro_metavars_in_unsafe/default/test.rs
Normal file
260
tests/ui-toml/macro_metavars_in_unsafe/default/test.rs
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
//! Tests macro_metavars_in_unsafe with default configuration
|
||||||
|
#![feature(decl_macro, lint_reasons)]
|
||||||
|
#![warn(clippy::macro_metavars_in_unsafe)]
|
||||||
|
#![allow(clippy::no_effect)]
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! allow_works {
|
||||||
|
($v:expr) => {
|
||||||
|
#[expect(clippy::macro_metavars_in_unsafe)]
|
||||||
|
unsafe {
|
||||||
|
$v;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! simple {
|
||||||
|
($v:expr) => {
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
dbg!($v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
#[rustfmt::skip] // for some reason rustfmt rewrites $r#unsafe to r#u$nsafe, bug?
|
||||||
|
macro_rules! raw_symbol {
|
||||||
|
($r#mod:expr, $r#unsafe:expr) => {
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
$r#mod;
|
||||||
|
}
|
||||||
|
$r#unsafe;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! multilevel_unsafe {
|
||||||
|
($v:expr) => {
|
||||||
|
unsafe {
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
$v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! in_function {
|
||||||
|
($v:expr) => {
|
||||||
|
unsafe {
|
||||||
|
fn f() {
|
||||||
|
// function introduces a new body, so don't lint.
|
||||||
|
$v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! in_function_with_unsafe {
|
||||||
|
($v:expr) => {
|
||||||
|
unsafe {
|
||||||
|
fn f() {
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
$v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! const_static {
|
||||||
|
($c:expr, $s:expr) => {
|
||||||
|
unsafe {
|
||||||
|
// const and static introduces new body, don't lint
|
||||||
|
const _X: i32 = $c;
|
||||||
|
static _Y: i32 = $s;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! const_generic_in_struct {
|
||||||
|
($inside_unsafe:expr, $outside_unsafe:expr) => {
|
||||||
|
unsafe {
|
||||||
|
struct Ty<
|
||||||
|
const L: i32 = 1,
|
||||||
|
const M: i32 = {
|
||||||
|
1;
|
||||||
|
unsafe { $inside_unsafe }
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
},
|
||||||
|
const N: i32 = { $outside_unsafe },
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! fn_with_const_generic {
|
||||||
|
($inside_unsafe:expr, $outside_unsafe:expr) => {
|
||||||
|
unsafe {
|
||||||
|
fn f<const N: usize>() {
|
||||||
|
$outside_unsafe;
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
$inside_unsafe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! variables {
|
||||||
|
($inside_unsafe:expr, $outside_unsafe:expr) => {
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
$inside_unsafe;
|
||||||
|
let inside_unsafe = 1;
|
||||||
|
inside_unsafe;
|
||||||
|
}
|
||||||
|
$outside_unsafe;
|
||||||
|
let outside_unsafe = 1;
|
||||||
|
outside_unsafe;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! multiple_matchers {
|
||||||
|
($inside_unsafe:expr, $outside_unsafe:expr) => {
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
$inside_unsafe;
|
||||||
|
}
|
||||||
|
$outside_unsafe;
|
||||||
|
};
|
||||||
|
($($v:expr, $x:expr),+) => {
|
||||||
|
$(
|
||||||
|
$v;
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
$x;
|
||||||
|
}
|
||||||
|
);+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! multiple_unsafe_blocks {
|
||||||
|
($w:expr, $x:expr, $y:expr) => {
|
||||||
|
$w;
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
$x;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
$x;
|
||||||
|
$y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub macro macro2_0($v:expr) {
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
$v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't lint private macros with the default configuration
|
||||||
|
macro_rules! private_mac {
|
||||||
|
($v:expr) => {
|
||||||
|
unsafe {
|
||||||
|
$v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't lint exported macros that are doc(hidden) because they also aren't part of the public API
|
||||||
|
#[macro_export]
|
||||||
|
#[doc(hidden)]
|
||||||
|
macro_rules! exported_but_hidden {
|
||||||
|
($v:expr) => {
|
||||||
|
unsafe {
|
||||||
|
$v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't lint if the same metavariable is expanded in an unsafe block and then outside of one:
|
||||||
|
// unsafe {} is still needed at callsite so not problematic
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! does_require_unsafe {
|
||||||
|
($v:expr) => {
|
||||||
|
unsafe {
|
||||||
|
$v;
|
||||||
|
}
|
||||||
|
$v;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! unsafe_from_root_ctxt {
|
||||||
|
($v:expr) => {
|
||||||
|
// Expands to unsafe { 1 }, but the unsafe block is from the root ctxt and not this macro,
|
||||||
|
// so no warning.
|
||||||
|
$v;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoked from another macro, should still generate a warning
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! nested_macro_helper {
|
||||||
|
($v:expr) => {{
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
$v;
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! nested_macros {
|
||||||
|
($v:expr, $v2:expr) => {{
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
nested_macro_helper!($v);
|
||||||
|
$v;
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
allow_works!(1);
|
||||||
|
simple!(1);
|
||||||
|
raw_symbol!(1, 1);
|
||||||
|
multilevel_unsafe!(1);
|
||||||
|
in_function!(1);
|
||||||
|
in_function_with_unsafe!(1);
|
||||||
|
const_static!(1, 1);
|
||||||
|
const_generic_in_struct!(1, 1);
|
||||||
|
fn_with_const_generic!(1, 1);
|
||||||
|
variables!(1, 1);
|
||||||
|
multiple_matchers!(1, 1);
|
||||||
|
multiple_matchers!(1, 1, 1, 1);
|
||||||
|
macro2_0!(1);
|
||||||
|
private_mac!(1);
|
||||||
|
exported_but_hidden!(1);
|
||||||
|
does_require_unsafe!(1);
|
||||||
|
multiple_unsafe_blocks!(1, 1, 1);
|
||||||
|
unsafe_from_root_ctxt!(unsafe { 1 });
|
||||||
|
nested_macros!(1, 1);
|
||||||
|
}
|
187
tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr
Normal file
187
tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:19:9
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | dbg!($v);
|
||||||
|
LL | | }
|
||||||
|
| |_________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
= note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]`
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:30:9
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | $r#mod;
|
||||||
|
LL | | }
|
||||||
|
| |_________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:42:13
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | $v;
|
||||||
|
LL | | }
|
||||||
|
| |_____________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:67:17
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | $v;
|
||||||
|
LL | | }
|
||||||
|
| |_________________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:95:21
|
||||||
|
|
|
||||||
|
LL | unsafe { $inside_unsafe }
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:110:17
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | $inside_unsafe;
|
||||||
|
LL | | }
|
||||||
|
| |_________________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:122:9
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | $inside_unsafe;
|
||||||
|
LL | | let inside_unsafe = 1;
|
||||||
|
LL | | inside_unsafe;
|
||||||
|
LL | | }
|
||||||
|
| |_________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:137:9
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | $inside_unsafe;
|
||||||
|
LL | | }
|
||||||
|
| |_________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:146:13
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | $x;
|
||||||
|
LL | | }
|
||||||
|
| |_____________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:171:5
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | $v;
|
||||||
|
LL | | }
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:158:9
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | $x;
|
||||||
|
LL | | }
|
||||||
|
| |_________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:162:9
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | $x;
|
||||||
|
LL | | $y;
|
||||||
|
LL | | }
|
||||||
|
| |_________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:222:9
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | $v;
|
||||||
|
LL | | }
|
||||||
|
| |_________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:232:9
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | nested_macro_helper!($v);
|
||||||
|
LL | | $v;
|
||||||
|
LL | | }
|
||||||
|
| |_________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
|
||||||
|
error: aborting due to 14 previous errors
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
warn-unsafe-macro-metavars-in-private-macros = true
|
15
tests/ui-toml/macro_metavars_in_unsafe/private/test.rs
Normal file
15
tests/ui-toml/macro_metavars_in_unsafe/private/test.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
//! Tests macro_metavars_in_unsafe with private (non-exported) macros
|
||||||
|
#![warn(clippy::macro_metavars_in_unsafe)]
|
||||||
|
|
||||||
|
macro_rules! mac {
|
||||||
|
($v:expr) => {
|
||||||
|
unsafe {
|
||||||
|
//~^ ERROR: this macro expands metavariables in an unsafe block
|
||||||
|
dbg!($v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mac!(1);
|
||||||
|
}
|
17
tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr
Normal file
17
tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
error: this macro expands metavariables in an unsafe block
|
||||||
|
--> tests/ui-toml/macro_metavars_in_unsafe/private/test.rs:6:9
|
||||||
|
|
|
||||||
|
LL | / unsafe {
|
||||||
|
LL | |
|
||||||
|
LL | | dbg!($v);
|
||||||
|
LL | | }
|
||||||
|
| |_________^
|
||||||
|
|
|
||||||
|
= note: this allows the user of the macro to write unsafe code outside of an unsafe block
|
||||||
|
= help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
|
||||||
|
= help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
|
||||||
|
= note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Ignore `From`, `TryFrom`, `FromStr` by default
|
||||||
|
# allow-renamed-params-for = []
|
2
tests/ui-toml/renamed_function_params/extend/clippy.toml
Normal file
2
tests/ui-toml/renamed_function_params/extend/clippy.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Ignore `From`, `TryFrom`, `FromStr` by default
|
||||||
|
allow-renamed-params-for = [ "..", "std::ops::Add", "renamed_function_params::MyTrait" ]
|
|
@ -0,0 +1,46 @@
|
||||||
|
error: renamed function parameter of trait impl
|
||||||
|
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:30:18
|
||||||
|
|
|
||||||
|
LL | fn eq(&self, rhs: &Self) -> bool {
|
||||||
|
| ^^^ help: consider using the default name: `other`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::renamed-function-params` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]`
|
||||||
|
|
||||||
|
error: renamed function parameter of trait impl
|
||||||
|
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:34:18
|
||||||
|
|
|
||||||
|
LL | fn ne(&self, rhs: &Self) -> bool {
|
||||||
|
| ^^^ help: consider using the default name: `other`
|
||||||
|
|
||||||
|
error: renamed function parameter of trait impl
|
||||||
|
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:48:19
|
||||||
|
|
|
||||||
|
LL | fn foo(&self, i_dont_wanna_use_your_name: u8) {} // only lint in `extend`
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the default name: `val`
|
||||||
|
|
||||||
|
error: renamed function parameter of trait impl
|
||||||
|
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:55:31
|
||||||
|
|
|
||||||
|
LL | fn hash<H: Hasher>(&self, states: &mut H) {
|
||||||
|
| ^^^^^^ help: consider using the default name: `state`
|
||||||
|
|
||||||
|
error: renamed function parameters of trait impl
|
||||||
|
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:59:30
|
||||||
|
|
|
||||||
|
LL | fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
|
||||||
|
| ^^^^ ^^^^^^
|
||||||
|
|
|
||||||
|
help: consider using the default names
|
||||||
|
|
|
||||||
|
LL | fn hash_slice<H: Hasher>(data: &[Self], state: &mut H) {
|
||||||
|
| ~~~~ ~~~~~
|
||||||
|
|
||||||
|
error: renamed function parameter of trait impl
|
||||||
|
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:80:18
|
||||||
|
|
|
||||||
|
LL | fn add(self, b: B) -> C {
|
||||||
|
| ^ help: consider using the default name: `rhs`
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
error: renamed function parameter of trait impl
|
||||||
|
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:30:18
|
||||||
|
|
|
||||||
|
LL | fn eq(&self, rhs: &Self) -> bool {
|
||||||
|
| ^^^ help: consider using the default name: `other`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::renamed-function-params` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]`
|
||||||
|
|
||||||
|
error: renamed function parameter of trait impl
|
||||||
|
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:34:18
|
||||||
|
|
|
||||||
|
LL | fn ne(&self, rhs: &Self) -> bool {
|
||||||
|
| ^^^ help: consider using the default name: `other`
|
||||||
|
|
||||||
|
error: renamed function parameter of trait impl
|
||||||
|
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:55:31
|
||||||
|
|
|
||||||
|
LL | fn hash<H: Hasher>(&self, states: &mut H) {
|
||||||
|
| ^^^^^^ help: consider using the default name: `state`
|
||||||
|
|
||||||
|
error: renamed function parameters of trait impl
|
||||||
|
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:59:30
|
||||||
|
|
|
||||||
|
LL | fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
|
||||||
|
| ^^^^ ^^^^^^
|
||||||
|
|
|
||||||
|
help: consider using the default names
|
||||||
|
|
|
||||||
|
LL | fn hash_slice<H: Hasher>(data: &[Self], state: &mut H) {
|
||||||
|
| ~~~~ ~~~~~
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
110
tests/ui-toml/renamed_function_params/renamed_function_params.rs
Normal file
110
tests/ui-toml/renamed_function_params/renamed_function_params.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
//@no-rustfix
|
||||||
|
//@revisions: default extend
|
||||||
|
//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/renamed_function_params/default
|
||||||
|
//@[extend] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/renamed_function_params/extend
|
||||||
|
#![warn(clippy::renamed_function_params)]
|
||||||
|
#![allow(clippy::partialeq_ne_impl, clippy::to_string_trait_impl)]
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
struct A;
|
||||||
|
impl From<A> for String {
|
||||||
|
fn from(_value: A) -> Self {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ToString for A {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct B(u32);
|
||||||
|
impl std::convert::From<B> for String {
|
||||||
|
fn from(b: B) -> Self {
|
||||||
|
b.0.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PartialEq for B {
|
||||||
|
fn eq(&self, rhs: &Self) -> bool {
|
||||||
|
//~^ ERROR: renamed function parameter of trait impl
|
||||||
|
self.0 == rhs.0
|
||||||
|
}
|
||||||
|
fn ne(&self, rhs: &Self) -> bool {
|
||||||
|
//~^ ERROR: renamed function parameter of trait impl
|
||||||
|
self.0 != rhs.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait MyTrait {
|
||||||
|
fn foo(&self, val: u8);
|
||||||
|
fn bar(a: u8, b: u8);
|
||||||
|
fn baz(self, _val: u8);
|
||||||
|
fn quz(&self, _: u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyTrait for B {
|
||||||
|
fn foo(&self, i_dont_wanna_use_your_name: u8) {} // only lint in `extend`
|
||||||
|
fn bar(_a: u8, _: u8) {}
|
||||||
|
fn baz(self, val: u8) {}
|
||||||
|
fn quz(&self, val: u8) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for B {
|
||||||
|
fn hash<H: Hasher>(&self, states: &mut H) {
|
||||||
|
//~^ ERROR: renamed function parameter of trait impl
|
||||||
|
self.0.hash(states);
|
||||||
|
}
|
||||||
|
fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
|
||||||
|
//~^ ERROR: renamed function parameters of trait impl
|
||||||
|
for d in date {
|
||||||
|
d.hash(states);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl B {
|
||||||
|
fn totally_irrelevant(&self, right: bool) {}
|
||||||
|
fn some_fn(&self, other: impl MyTrait) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum C {
|
||||||
|
A,
|
||||||
|
B(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Add<B> for C {
|
||||||
|
type Output = C;
|
||||||
|
fn add(self, b: B) -> C {
|
||||||
|
// only lint in `extend`
|
||||||
|
C::B(b.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<A> for C {
|
||||||
|
fn from(_: A) -> C {
|
||||||
|
C::A
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait CustomTraitA {
|
||||||
|
fn foo(&self, other: u32);
|
||||||
|
}
|
||||||
|
trait CustomTraitB {
|
||||||
|
fn bar(&self, value: u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_trait {
|
||||||
|
($impl_for:ident, $tr:ty, $fn_name:ident, $t:ty) => {
|
||||||
|
impl $tr for $impl_for {
|
||||||
|
fn $fn_name(&self, v: $t) {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_trait!(C, CustomTraitA, foo, u32);
|
||||||
|
impl_trait!(C, CustomTraitB, bar, u8);
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -40,3 +40,9 @@ fn main() {
|
||||||
let _ = HashMap;
|
let _ = HashMap;
|
||||||
let _: usize = 64_usize;
|
let _: usize = 64_usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod useless_attribute {
|
||||||
|
// Regression test for https://github.com/rust-lang/rust-clippy/issues/12753
|
||||||
|
#[allow(clippy::disallowed_types)]
|
||||||
|
use std::collections::HashMap;
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
|
||||||
allow-one-hash-in-raw-strings
|
allow-one-hash-in-raw-strings
|
||||||
allow-print-in-tests
|
allow-print-in-tests
|
||||||
allow-private-module-inception
|
allow-private-module-inception
|
||||||
|
allow-renamed-params-for
|
||||||
allow-unwrap-in-tests
|
allow-unwrap-in-tests
|
||||||
allow-useless-vec-in-tests
|
allow-useless-vec-in-tests
|
||||||
allowed-dotfiles
|
allowed-dotfiles
|
||||||
|
@ -74,6 +75,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
|
||||||
vec-box-size-threshold
|
vec-box-size-threshold
|
||||||
verbose-bit-mask-threshold
|
verbose-bit-mask-threshold
|
||||||
warn-on-all-wildcard-imports
|
warn-on-all-wildcard-imports
|
||||||
|
warn-unsafe-macro-metavars-in-private-macros
|
||||||
--> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:2:1
|
--> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:2:1
|
||||||
|
|
|
|
||||||
LL | foobar = 42
|
LL | foobar = 42
|
||||||
|
@ -91,6 +93,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
|
||||||
allow-one-hash-in-raw-strings
|
allow-one-hash-in-raw-strings
|
||||||
allow-print-in-tests
|
allow-print-in-tests
|
||||||
allow-private-module-inception
|
allow-private-module-inception
|
||||||
|
allow-renamed-params-for
|
||||||
allow-unwrap-in-tests
|
allow-unwrap-in-tests
|
||||||
allow-useless-vec-in-tests
|
allow-useless-vec-in-tests
|
||||||
allowed-dotfiles
|
allowed-dotfiles
|
||||||
|
@ -155,6 +158,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
|
||||||
vec-box-size-threshold
|
vec-box-size-threshold
|
||||||
verbose-bit-mask-threshold
|
verbose-bit-mask-threshold
|
||||||
warn-on-all-wildcard-imports
|
warn-on-all-wildcard-imports
|
||||||
|
warn-unsafe-macro-metavars-in-private-macros
|
||||||
--> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:4:1
|
--> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:4:1
|
||||||
|
|
|
|
||||||
LL | barfoo = 53
|
LL | barfoo = 53
|
||||||
|
@ -172,6 +176,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
|
||||||
allow-one-hash-in-raw-strings
|
allow-one-hash-in-raw-strings
|
||||||
allow-print-in-tests
|
allow-print-in-tests
|
||||||
allow-private-module-inception
|
allow-private-module-inception
|
||||||
|
allow-renamed-params-for
|
||||||
allow-unwrap-in-tests
|
allow-unwrap-in-tests
|
||||||
allow-useless-vec-in-tests
|
allow-useless-vec-in-tests
|
||||||
allowed-dotfiles
|
allowed-dotfiles
|
||||||
|
@ -236,6 +241,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
|
||||||
vec-box-size-threshold
|
vec-box-size-threshold
|
||||||
verbose-bit-mask-threshold
|
verbose-bit-mask-threshold
|
||||||
warn-on-all-wildcard-imports
|
warn-on-all-wildcard-imports
|
||||||
|
warn-unsafe-macro-metavars-in-private-macros
|
||||||
--> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:7:1
|
--> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:7:1
|
||||||
|
|
|
|
||||||
LL | allow_mixed_uninlined_format_args = true
|
LL | allow_mixed_uninlined_format_args = true
|
||||||
|
|
|
@ -62,6 +62,16 @@ fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFr
|
||||||
mut_thing.clone_from(ref_thing + ref_thing);
|
mut_thing.clone_from(ref_thing + ref_thing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clone_method_macro() {
|
||||||
|
let mut s = String::from("");
|
||||||
|
s.clone_from(&format!("{} {}", "hello", "world"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_function_macro() {
|
||||||
|
let mut s = String::from("");
|
||||||
|
Clone::clone_from(&mut s, &format!("{} {}", "hello", "world"));
|
||||||
|
}
|
||||||
|
|
||||||
fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom {
|
fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom {
|
||||||
let mut a = HasCloneFrom;
|
let mut a = HasCloneFrom;
|
||||||
for _ in 1..10 {
|
for _ in 1..10 {
|
||||||
|
@ -86,6 +96,12 @@ fn assign_to_uninit_mut_var(b: HasCloneFrom) {
|
||||||
a = b.clone();
|
a = b.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn late_init_let_tuple() {
|
||||||
|
let (p, q): (String, String);
|
||||||
|
p = "ghi".to_string();
|
||||||
|
q = p.clone();
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HasDeriveClone;
|
pub struct HasDeriveClone;
|
||||||
|
|
||||||
|
@ -208,6 +224,16 @@ fn owned_function_val(mut mut_thing: String, ref_str: &str) {
|
||||||
ToOwned::clone_into(ref_str, &mut mut_thing);
|
ToOwned::clone_into(ref_str, &mut mut_thing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn owned_method_macro() {
|
||||||
|
let mut s = String::from("");
|
||||||
|
format!("{} {}", "hello", "world").clone_into(&mut s);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn owned_function_macro() {
|
||||||
|
let mut s = String::from("");
|
||||||
|
ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s);
|
||||||
|
}
|
||||||
|
|
||||||
struct FakeToOwned;
|
struct FakeToOwned;
|
||||||
impl FakeToOwned {
|
impl FakeToOwned {
|
||||||
/// This looks just like `ToOwned::to_owned`
|
/// This looks just like `ToOwned::to_owned`
|
||||||
|
|
|
@ -62,6 +62,16 @@ fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFr
|
||||||
*mut_thing = (ref_thing + ref_thing).clone();
|
*mut_thing = (ref_thing + ref_thing).clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clone_method_macro() {
|
||||||
|
let mut s = String::from("");
|
||||||
|
s = format!("{} {}", "hello", "world").clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_function_macro() {
|
||||||
|
let mut s = String::from("");
|
||||||
|
s = Clone::clone(&format!("{} {}", "hello", "world"));
|
||||||
|
}
|
||||||
|
|
||||||
fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom {
|
fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom {
|
||||||
let mut a = HasCloneFrom;
|
let mut a = HasCloneFrom;
|
||||||
for _ in 1..10 {
|
for _ in 1..10 {
|
||||||
|
@ -86,6 +96,12 @@ fn assign_to_uninit_mut_var(b: HasCloneFrom) {
|
||||||
a = b.clone();
|
a = b.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn late_init_let_tuple() {
|
||||||
|
let (p, q): (String, String);
|
||||||
|
p = "ghi".to_string();
|
||||||
|
q = p.clone();
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HasDeriveClone;
|
pub struct HasDeriveClone;
|
||||||
|
|
||||||
|
@ -208,6 +224,16 @@ fn owned_function_val(mut mut_thing: String, ref_str: &str) {
|
||||||
mut_thing = ToOwned::to_owned(ref_str);
|
mut_thing = ToOwned::to_owned(ref_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn owned_method_macro() {
|
||||||
|
let mut s = String::from("");
|
||||||
|
s = format!("{} {}", "hello", "world").to_owned();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn owned_function_macro() {
|
||||||
|
let mut s = String::from("");
|
||||||
|
s = ToOwned::to_owned(&format!("{} {}", "hello", "world"));
|
||||||
|
}
|
||||||
|
|
||||||
struct FakeToOwned;
|
struct FakeToOwned;
|
||||||
impl FakeToOwned {
|
impl FakeToOwned {
|
||||||
/// This looks just like `ToOwned::to_owned`
|
/// This looks just like `ToOwned::to_owned`
|
||||||
|
|
|
@ -62,64 +62,88 @@ LL | *mut_thing = (ref_thing + ref_thing).clone();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing + ref_thing)`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing + ref_thing)`
|
||||||
|
|
||||||
error: assigning the result of `Clone::clone()` may be inefficient
|
error: assigning the result of `Clone::clone()` may be inefficient
|
||||||
--> tests/ui/assigning_clones.rs:68:9
|
--> tests/ui/assigning_clones.rs:67:5
|
||||||
|
|
|
||||||
|
LL | s = format!("{} {}", "hello", "world").clone();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `s.clone_from(&format!("{} {}", "hello", "world"))`
|
||||||
|
|
||||||
|
error: assigning the result of `Clone::clone()` may be inefficient
|
||||||
|
--> tests/ui/assigning_clones.rs:72:5
|
||||||
|
|
|
||||||
|
LL | s = Clone::clone(&format!("{} {}", "hello", "world"));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut s, &format!("{} {}", "hello", "world"))`
|
||||||
|
|
||||||
|
error: assigning the result of `Clone::clone()` may be inefficient
|
||||||
|
--> tests/ui/assigning_clones.rs:78:9
|
||||||
|
|
|
|
||||||
LL | a = b.clone();
|
LL | a = b.clone();
|
||||||
| ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
|
| ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
|
||||||
|
|
||||||
error: assigning the result of `Clone::clone()` may be inefficient
|
error: assigning the result of `Clone::clone()` may be inefficient
|
||||||
--> tests/ui/assigning_clones.rs:133:5
|
--> tests/ui/assigning_clones.rs:149:5
|
||||||
|
|
|
|
||||||
LL | a = b.clone();
|
LL | a = b.clone();
|
||||||
| ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
|
| ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
|
||||||
|
|
||||||
error: assigning the result of `Clone::clone()` may be inefficient
|
error: assigning the result of `Clone::clone()` may be inefficient
|
||||||
--> tests/ui/assigning_clones.rs:140:5
|
--> tests/ui/assigning_clones.rs:156:5
|
||||||
|
|
|
|
||||||
LL | a = b.clone();
|
LL | a = b.clone();
|
||||||
| ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
|
| ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
|
||||||
|
|
||||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||||
--> tests/ui/assigning_clones.rs:141:5
|
--> tests/ui/assigning_clones.rs:157:5
|
||||||
|
|
|
|
||||||
LL | a = c.to_owned();
|
LL | a = c.to_owned();
|
||||||
| ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)`
|
| ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)`
|
||||||
|
|
||||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||||
--> tests/ui/assigning_clones.rs:171:5
|
--> tests/ui/assigning_clones.rs:187:5
|
||||||
|
|
|
|
||||||
LL | *mut_string = ref_str.to_owned();
|
LL | *mut_string = ref_str.to_owned();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)`
|
||||||
|
|
||||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||||
--> tests/ui/assigning_clones.rs:175:5
|
--> tests/ui/assigning_clones.rs:191:5
|
||||||
|
|
|
|
||||||
LL | mut_string = ref_str.to_owned();
|
LL | mut_string = ref_str.to_owned();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)`
|
||||||
|
|
||||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||||
--> tests/ui/assigning_clones.rs:196:5
|
--> tests/ui/assigning_clones.rs:212:5
|
||||||
|
|
|
|
||||||
LL | **mut_box_string = ref_str.to_owned();
|
LL | **mut_box_string = ref_str.to_owned();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))`
|
||||||
|
|
||||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||||
--> tests/ui/assigning_clones.rs:200:5
|
--> tests/ui/assigning_clones.rs:216:5
|
||||||
|
|
|
|
||||||
LL | **mut_box_string = ref_str.to_owned();
|
LL | **mut_box_string = ref_str.to_owned();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))`
|
||||||
|
|
||||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||||
--> tests/ui/assigning_clones.rs:204:5
|
--> tests/ui/assigning_clones.rs:220:5
|
||||||
|
|
|
|
||||||
LL | *mut_thing = ToOwned::to_owned(ref_str);
|
LL | *mut_thing = ToOwned::to_owned(ref_str);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)`
|
||||||
|
|
||||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||||
--> tests/ui/assigning_clones.rs:208:5
|
--> tests/ui/assigning_clones.rs:224:5
|
||||||
|
|
|
|
||||||
LL | mut_thing = ToOwned::to_owned(ref_str);
|
LL | mut_thing = ToOwned::to_owned(ref_str);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)`
|
||||||
|
|
||||||
error: aborting due to 20 previous errors
|
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||||
|
--> tests/ui/assigning_clones.rs:229:5
|
||||||
|
|
|
||||||
|
LL | s = format!("{} {}", "hello", "world").to_owned();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `format!("{} {}", "hello", "world").clone_into(&mut s)`
|
||||||
|
|
||||||
|
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||||
|
--> tests/ui/assigning_clones.rs:234:5
|
||||||
|
|
|
||||||
|
LL | s = ToOwned::to_owned(&format!("{} {}", "hello", "world"));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s)`
|
||||||
|
|
||||||
|
error: aborting due to 24 previous errors
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#![allow(clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, dead_code)]
|
#![allow(clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, dead_code)]
|
||||||
#![warn(clippy::expl_impl_clone_on_copy)]
|
#![warn(clippy::expl_impl_clone_on_copy)]
|
||||||
|
|
||||||
|
|
||||||
#[derive(Copy)]
|
#[derive(Copy)]
|
||||||
struct Qux;
|
struct Qux;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||||
--> tests/ui/derive.rs:8:1
|
--> tests/ui/derive.rs:7:1
|
||||||
|
|
|
|
||||||
LL | / impl Clone for Qux {
|
LL | / impl Clone for Qux {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -10,7 +10,7 @@ LL | | }
|
||||||
| |_^
|
| |_^
|
||||||
|
|
|
|
||||||
note: consider deriving `Clone` or removing `Copy`
|
note: consider deriving `Clone` or removing `Copy`
|
||||||
--> tests/ui/derive.rs:8:1
|
--> tests/ui/derive.rs:7:1
|
||||||
|
|
|
|
||||||
LL | / impl Clone for Qux {
|
LL | / impl Clone for Qux {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -23,7 +23,7 @@ LL | | }
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]`
|
= help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]`
|
||||||
|
|
||||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||||
--> tests/ui/derive.rs:33:1
|
--> tests/ui/derive.rs:32:1
|
||||||
|
|
|
|
||||||
LL | / impl<'a> Clone for Lt<'a> {
|
LL | / impl<'a> Clone for Lt<'a> {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -34,7 +34,7 @@ LL | | }
|
||||||
| |_^
|
| |_^
|
||||||
|
|
|
|
||||||
note: consider deriving `Clone` or removing `Copy`
|
note: consider deriving `Clone` or removing `Copy`
|
||||||
--> tests/ui/derive.rs:33:1
|
--> tests/ui/derive.rs:32:1
|
||||||
|
|
|
|
||||||
LL | / impl<'a> Clone for Lt<'a> {
|
LL | / impl<'a> Clone for Lt<'a> {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -45,7 +45,7 @@ LL | | }
|
||||||
| |_^
|
| |_^
|
||||||
|
|
||||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||||
--> tests/ui/derive.rs:45:1
|
--> tests/ui/derive.rs:44:1
|
||||||
|
|
|
|
||||||
LL | / impl Clone for BigArray {
|
LL | / impl Clone for BigArray {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -56,7 +56,7 @@ LL | | }
|
||||||
| |_^
|
| |_^
|
||||||
|
|
|
|
||||||
note: consider deriving `Clone` or removing `Copy`
|
note: consider deriving `Clone` or removing `Copy`
|
||||||
--> tests/ui/derive.rs:45:1
|
--> tests/ui/derive.rs:44:1
|
||||||
|
|
|
|
||||||
LL | / impl Clone for BigArray {
|
LL | / impl Clone for BigArray {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -67,7 +67,7 @@ LL | | }
|
||||||
| |_^
|
| |_^
|
||||||
|
|
||||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||||
--> tests/ui/derive.rs:57:1
|
--> tests/ui/derive.rs:56:1
|
||||||
|
|
|
|
||||||
LL | / impl Clone for FnPtr {
|
LL | / impl Clone for FnPtr {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -78,7 +78,7 @@ LL | | }
|
||||||
| |_^
|
| |_^
|
||||||
|
|
|
|
||||||
note: consider deriving `Clone` or removing `Copy`
|
note: consider deriving `Clone` or removing `Copy`
|
||||||
--> tests/ui/derive.rs:57:1
|
--> tests/ui/derive.rs:56:1
|
||||||
|
|
|
|
||||||
LL | / impl Clone for FnPtr {
|
LL | / impl Clone for FnPtr {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -89,7 +89,7 @@ LL | | }
|
||||||
| |_^
|
| |_^
|
||||||
|
|
||||||
error: you are implementing `Clone` explicitly on a `Copy` type
|
error: you are implementing `Clone` explicitly on a `Copy` type
|
||||||
--> tests/ui/derive.rs:78:1
|
--> tests/ui/derive.rs:77:1
|
||||||
|
|
|
|
||||||
LL | / impl<T: Clone> Clone for Generic2<T> {
|
LL | / impl<T: Clone> Clone for Generic2<T> {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -100,7 +100,7 @@ LL | | }
|
||||||
| |_^
|
| |_^
|
||||||
|
|
|
|
||||||
note: consider deriving `Clone` or removing `Copy`
|
note: consider deriving `Clone` or removing `Copy`
|
||||||
--> tests/ui/derive.rs:78:1
|
--> tests/ui/derive.rs:77:1
|
||||||
|
|
|
|
||||||
LL | / impl<T: Clone> Clone for Generic2<T> {
|
LL | / impl<T: Clone> Clone for Generic2<T> {
|
||||||
LL | |
|
LL | |
|
||||||
|
|
47
tests/ui/doc/doc_lazy_blockquote.fixed
Normal file
47
tests/ui/doc/doc_lazy_blockquote.fixed
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#![warn(clippy::doc_lazy_continuation)]
|
||||||
|
|
||||||
|
/// > blockquote with
|
||||||
|
/// > lazy continuation
|
||||||
|
//~^ ERROR: doc quote missing `>` marker
|
||||||
|
fn first() {}
|
||||||
|
|
||||||
|
/// > blockquote with no
|
||||||
|
/// > lazy continuation
|
||||||
|
fn first_nowarn() {}
|
||||||
|
|
||||||
|
/// > blockquote with no
|
||||||
|
///
|
||||||
|
/// lazy continuation
|
||||||
|
fn two_nowarn() {}
|
||||||
|
|
||||||
|
/// > nest here
|
||||||
|
/// >
|
||||||
|
/// > > nest here
|
||||||
|
/// > > lazy continuation
|
||||||
|
//~^ ERROR: doc quote missing `>` marker
|
||||||
|
fn two() {}
|
||||||
|
|
||||||
|
/// > nest here
|
||||||
|
/// >
|
||||||
|
/// > > nest here
|
||||||
|
/// > > lazy continuation
|
||||||
|
//~^ ERROR: doc quote missing `>` marker
|
||||||
|
fn three() {}
|
||||||
|
|
||||||
|
/// > * > nest here
|
||||||
|
/// > > lazy continuation
|
||||||
|
//~^ ERROR: doc quote missing `>` marker
|
||||||
|
fn four() {}
|
||||||
|
|
||||||
|
/// > * > nest here
|
||||||
|
/// > > lazy continuation
|
||||||
|
//~^ ERROR: doc quote missing `>` marker
|
||||||
|
fn four_point_1() {}
|
||||||
|
|
||||||
|
/// * > nest here lazy continuation
|
||||||
|
fn five() {}
|
||||||
|
|
||||||
|
/// 1. > nest here
|
||||||
|
/// > lazy continuation (this results in strange indentation, but still works)
|
||||||
|
//~^ ERROR: doc quote missing `>` marker
|
||||||
|
fn six() {}
|
47
tests/ui/doc/doc_lazy_blockquote.rs
Normal file
47
tests/ui/doc/doc_lazy_blockquote.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#![warn(clippy::doc_lazy_continuation)]
|
||||||
|
|
||||||
|
/// > blockquote with
|
||||||
|
/// lazy continuation
|
||||||
|
//~^ ERROR: doc quote missing `>` marker
|
||||||
|
fn first() {}
|
||||||
|
|
||||||
|
/// > blockquote with no
|
||||||
|
/// > lazy continuation
|
||||||
|
fn first_nowarn() {}
|
||||||
|
|
||||||
|
/// > blockquote with no
|
||||||
|
///
|
||||||
|
/// lazy continuation
|
||||||
|
fn two_nowarn() {}
|
||||||
|
|
||||||
|
/// > nest here
|
||||||
|
/// >
|
||||||
|
/// > > nest here
|
||||||
|
/// > lazy continuation
|
||||||
|
//~^ ERROR: doc quote missing `>` marker
|
||||||
|
fn two() {}
|
||||||
|
|
||||||
|
/// > nest here
|
||||||
|
/// >
|
||||||
|
/// > > nest here
|
||||||
|
/// lazy continuation
|
||||||
|
//~^ ERROR: doc quote missing `>` marker
|
||||||
|
fn three() {}
|
||||||
|
|
||||||
|
/// > * > nest here
|
||||||
|
/// lazy continuation
|
||||||
|
//~^ ERROR: doc quote missing `>` marker
|
||||||
|
fn four() {}
|
||||||
|
|
||||||
|
/// > * > nest here
|
||||||
|
/// lazy continuation
|
||||||
|
//~^ ERROR: doc quote missing `>` marker
|
||||||
|
fn four_point_1() {}
|
||||||
|
|
||||||
|
/// * > nest here lazy continuation
|
||||||
|
fn five() {}
|
||||||
|
|
||||||
|
/// 1. > nest here
|
||||||
|
/// lazy continuation (this results in strange indentation, but still works)
|
||||||
|
//~^ ERROR: doc quote missing `>` marker
|
||||||
|
fn six() {}
|
76
tests/ui/doc/doc_lazy_blockquote.stderr
Normal file
76
tests/ui/doc/doc_lazy_blockquote.stderr
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
error: doc quote missing `>` marker
|
||||||
|
--> tests/ui/doc/doc_lazy_blockquote.rs:4:5
|
||||||
|
|
|
||||||
|
LL | /// lazy continuation
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this not intended to be a quote at all, escape it with `\>`
|
||||||
|
= note: `-D clippy::doc-lazy-continuation` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]`
|
||||||
|
help: add markers to start of line
|
||||||
|
|
|
||||||
|
LL | /// > lazy continuation
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: doc quote missing `>` marker
|
||||||
|
--> tests/ui/doc/doc_lazy_blockquote.rs:20:5
|
||||||
|
|
|
||||||
|
LL | /// > lazy continuation
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: if this not intended to be a quote at all, escape it with `\>`
|
||||||
|
help: add markers to start of line
|
||||||
|
|
|
||||||
|
LL | /// > > lazy continuation
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: doc quote missing `>` marker
|
||||||
|
--> tests/ui/doc/doc_lazy_blockquote.rs:27:5
|
||||||
|
|
|
||||||
|
LL | /// lazy continuation
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this not intended to be a quote at all, escape it with `\>`
|
||||||
|
help: add markers to start of line
|
||||||
|
|
|
||||||
|
LL | /// > > lazy continuation
|
||||||
|
| +++
|
||||||
|
|
||||||
|
error: doc quote missing `>` marker
|
||||||
|
--> tests/ui/doc/doc_lazy_blockquote.rs:32:5
|
||||||
|
|
|
||||||
|
LL | /// lazy continuation
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this not intended to be a quote at all, escape it with `\>`
|
||||||
|
help: add markers to start of line
|
||||||
|
|
|
||||||
|
LL | /// > > lazy continuation
|
||||||
|
| +++++++
|
||||||
|
|
||||||
|
error: doc quote missing `>` marker
|
||||||
|
--> tests/ui/doc/doc_lazy_blockquote.rs:37:5
|
||||||
|
|
|
||||||
|
LL | /// lazy continuation
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this not intended to be a quote at all, escape it with `\>`
|
||||||
|
help: add markers to start of line
|
||||||
|
|
|
||||||
|
LL | /// > > lazy continuation
|
||||||
|
| +++++
|
||||||
|
|
||||||
|
error: doc quote missing `>` marker
|
||||||
|
--> tests/ui/doc/doc_lazy_blockquote.rs:45:5
|
||||||
|
|
|
||||||
|
LL | /// lazy continuation (this results in strange indentation, but still works)
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this not intended to be a quote at all, escape it with `\>`
|
||||||
|
help: add markers to start of line
|
||||||
|
|
|
||||||
|
LL | /// > lazy continuation (this results in strange indentation, but still works)
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
42
tests/ui/doc/doc_lazy_list.fixed
Normal file
42
tests/ui/doc/doc_lazy_list.fixed
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#![warn(clippy::doc_lazy_continuation)]
|
||||||
|
|
||||||
|
/// 1. nest here
|
||||||
|
/// lazy continuation
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
fn one() {}
|
||||||
|
|
||||||
|
/// 1. first line
|
||||||
|
/// lazy list continuations don't make warnings with this lint
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
/// because they don't have the
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
fn two() {}
|
||||||
|
|
||||||
|
/// - nest here
|
||||||
|
/// lazy continuation
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
fn three() {}
|
||||||
|
|
||||||
|
/// - first line
|
||||||
|
/// lazy list continuations don't make warnings with this lint
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
/// because they don't have the
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
fn four() {}
|
||||||
|
|
||||||
|
/// - nest here
|
||||||
|
/// lazy continuation
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
fn five() {}
|
||||||
|
|
||||||
|
/// - - first line
|
||||||
|
/// this will warn on the lazy continuation
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
/// and so should this
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
fn six() {}
|
||||||
|
|
||||||
|
/// - - first line
|
||||||
|
///
|
||||||
|
/// this is not a lazy continuation
|
||||||
|
fn seven() {}
|
42
tests/ui/doc/doc_lazy_list.rs
Normal file
42
tests/ui/doc/doc_lazy_list.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#![warn(clippy::doc_lazy_continuation)]
|
||||||
|
|
||||||
|
/// 1. nest here
|
||||||
|
/// lazy continuation
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
fn one() {}
|
||||||
|
|
||||||
|
/// 1. first line
|
||||||
|
/// lazy list continuations don't make warnings with this lint
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
/// because they don't have the
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
fn two() {}
|
||||||
|
|
||||||
|
/// - nest here
|
||||||
|
/// lazy continuation
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
fn three() {}
|
||||||
|
|
||||||
|
/// - first line
|
||||||
|
/// lazy list continuations don't make warnings with this lint
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
/// because they don't have the
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
fn four() {}
|
||||||
|
|
||||||
|
/// - nest here
|
||||||
|
/// lazy continuation
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
fn five() {}
|
||||||
|
|
||||||
|
/// - - first line
|
||||||
|
/// this will warn on the lazy continuation
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
/// and so should this
|
||||||
|
//~^ ERROR: doc list item missing indentation
|
||||||
|
fn six() {}
|
||||||
|
|
||||||
|
/// - - first line
|
||||||
|
///
|
||||||
|
/// this is not a lazy continuation
|
||||||
|
fn seven() {}
|
112
tests/ui/doc/doc_lazy_list.stderr
Normal file
112
tests/ui/doc/doc_lazy_list.stderr
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
error: doc list item missing indentation
|
||||||
|
--> tests/ui/doc/doc_lazy_list.rs:4:5
|
||||||
|
|
|
||||||
|
LL | /// lazy continuation
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this is supposed to be its own paragraph, add a blank line
|
||||||
|
= note: `-D clippy::doc-lazy-continuation` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]`
|
||||||
|
help: indent this line
|
||||||
|
|
|
||||||
|
LL | /// lazy continuation
|
||||||
|
| +++
|
||||||
|
|
||||||
|
error: doc list item missing indentation
|
||||||
|
--> tests/ui/doc/doc_lazy_list.rs:9:5
|
||||||
|
|
|
||||||
|
LL | /// lazy list continuations don't make warnings with this lint
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this is supposed to be its own paragraph, add a blank line
|
||||||
|
help: indent this line
|
||||||
|
|
|
||||||
|
LL | /// lazy list continuations don't make warnings with this lint
|
||||||
|
| +++
|
||||||
|
|
||||||
|
error: doc list item missing indentation
|
||||||
|
--> tests/ui/doc/doc_lazy_list.rs:11:5
|
||||||
|
|
|
||||||
|
LL | /// because they don't have the
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this is supposed to be its own paragraph, add a blank line
|
||||||
|
help: indent this line
|
||||||
|
|
|
||||||
|
LL | /// because they don't have the
|
||||||
|
| +++
|
||||||
|
|
||||||
|
error: doc list item missing indentation
|
||||||
|
--> tests/ui/doc/doc_lazy_list.rs:16:5
|
||||||
|
|
|
||||||
|
LL | /// lazy continuation
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this is supposed to be its own paragraph, add a blank line
|
||||||
|
help: indent this line
|
||||||
|
|
|
||||||
|
LL | /// lazy continuation
|
||||||
|
| ++++
|
||||||
|
|
||||||
|
error: doc list item missing indentation
|
||||||
|
--> tests/ui/doc/doc_lazy_list.rs:21:5
|
||||||
|
|
|
||||||
|
LL | /// lazy list continuations don't make warnings with this lint
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this is supposed to be its own paragraph, add a blank line
|
||||||
|
help: indent this line
|
||||||
|
|
|
||||||
|
LL | /// lazy list continuations don't make warnings with this lint
|
||||||
|
| ++++
|
||||||
|
|
||||||
|
error: doc list item missing indentation
|
||||||
|
--> tests/ui/doc/doc_lazy_list.rs:23:5
|
||||||
|
|
|
||||||
|
LL | /// because they don't have the
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this is supposed to be its own paragraph, add a blank line
|
||||||
|
help: indent this line
|
||||||
|
|
|
||||||
|
LL | /// because they don't have the
|
||||||
|
| ++++
|
||||||
|
|
||||||
|
error: doc list item missing indentation
|
||||||
|
--> tests/ui/doc/doc_lazy_list.rs:28:5
|
||||||
|
|
|
||||||
|
LL | /// lazy continuation
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this is supposed to be its own paragraph, add a blank line
|
||||||
|
help: indent this line
|
||||||
|
|
|
||||||
|
LL | /// lazy continuation
|
||||||
|
| ++++
|
||||||
|
|
||||||
|
error: doc list item missing indentation
|
||||||
|
--> tests/ui/doc/doc_lazy_list.rs:33:5
|
||||||
|
|
|
||||||
|
LL | /// this will warn on the lazy continuation
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= help: if this is supposed to be its own paragraph, add a blank line
|
||||||
|
help: indent this line
|
||||||
|
|
|
||||||
|
LL | /// this will warn on the lazy continuation
|
||||||
|
| ++++++
|
||||||
|
|
||||||
|
error: doc list item missing indentation
|
||||||
|
--> tests/ui/doc/doc_lazy_list.rs:35:5
|
||||||
|
|
|
||||||
|
LL | /// and so should this
|
||||||
|
| ^^^^
|
||||||
|
|
|
||||||
|
= help: if this is supposed to be its own paragraph, add a blank line
|
||||||
|
help: indent this line
|
||||||
|
|
|
||||||
|
LL | /// and so should this
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error: aborting due to 9 previous errors
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//@aux-build:proc_macro_attr.rs
|
//@aux-build:proc_macro_attr.rs
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
#![warn(clippy::duplicated_attributes)]
|
#![warn(clippy::duplicated_attributes)]
|
||||||
#![cfg(any(unix, windows))]
|
#![cfg(any(unix, windows))]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
@ -20,6 +20,10 @@ fn foo() {}
|
||||||
#[cfg(unix)] // cfgs are not handled
|
#[cfg(unix)] // cfgs are not handled
|
||||||
fn bar() {}
|
fn bar() {}
|
||||||
|
|
||||||
|
// No warning:
|
||||||
|
#[rustc_on_unimplemented(on(_Self = "&str", label = "`a"), on(_Self = "alloc::string::String", label = "a"))]
|
||||||
|
trait Abc {}
|
||||||
|
|
||||||
#[proc_macro_attr::duplicated_attr()] // Should not warn!
|
#[proc_macro_attr::duplicated_attr()] // Should not warn!
|
||||||
fn babar() {}
|
fn babar() {}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![feature(const_int_from_str)]
|
||||||
#![warn(clippy::from_str_radix_10)]
|
#![warn(clippy::from_str_radix_10)]
|
||||||
|
|
||||||
mod some_mod {
|
mod some_mod {
|
||||||
|
@ -59,3 +60,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue_12732() {
|
||||||
|
const A: Result<u32, std::num::ParseIntError> = u32::from_str_radix("123", 10);
|
||||||
|
const B: () = {
|
||||||
|
let _ = u32::from_str_radix("123", 10);
|
||||||
|
};
|
||||||
|
const fn foo() {
|
||||||
|
let _ = u32::from_str_radix("123", 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![feature(const_int_from_str)]
|
||||||
#![warn(clippy::from_str_radix_10)]
|
#![warn(clippy::from_str_radix_10)]
|
||||||
|
|
||||||
mod some_mod {
|
mod some_mod {
|
||||||
|
@ -59,3 +60,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue_12732() {
|
||||||
|
const A: Result<u32, std::num::ParseIntError> = u32::from_str_radix("123", 10);
|
||||||
|
const B: () = {
|
||||||
|
let _ = u32::from_str_radix("123", 10);
|
||||||
|
};
|
||||||
|
const fn foo() {
|
||||||
|
let _ = u32::from_str_radix("123", 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
||||||
--> tests/ui/from_str_radix_10.rs:28:5
|
--> tests/ui/from_str_radix_10.rs:29:5
|
||||||
|
|
|
|
||||||
LL | u32::from_str_radix("30", 10)?;
|
LL | u32::from_str_radix("30", 10)?;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::<u32>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::<u32>()`
|
||||||
|
@ -8,43 +8,43 @@ LL | u32::from_str_radix("30", 10)?;
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::from_str_radix_10)]`
|
= help: to override `-D warnings` add `#[allow(clippy::from_str_radix_10)]`
|
||||||
|
|
||||||
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
||||||
--> tests/ui/from_str_radix_10.rs:31:5
|
--> tests/ui/from_str_radix_10.rs:32:5
|
||||||
|
|
|
|
||||||
LL | i64::from_str_radix("24", 10)?;
|
LL | i64::from_str_radix("24", 10)?;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::<i64>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::<i64>()`
|
||||||
|
|
||||||
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
||||||
--> tests/ui/from_str_radix_10.rs:33:5
|
--> tests/ui/from_str_radix_10.rs:34:5
|
||||||
|
|
|
|
||||||
LL | isize::from_str_radix("100", 10)?;
|
LL | isize::from_str_radix("100", 10)?;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::<isize>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::<isize>()`
|
||||||
|
|
||||||
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
||||||
--> tests/ui/from_str_radix_10.rs:35:5
|
--> tests/ui/from_str_radix_10.rs:36:5
|
||||||
|
|
|
|
||||||
LL | u8::from_str_radix("7", 10)?;
|
LL | u8::from_str_radix("7", 10)?;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::<u8>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::<u8>()`
|
||||||
|
|
||||||
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
||||||
--> tests/ui/from_str_radix_10.rs:37:5
|
--> tests/ui/from_str_radix_10.rs:38:5
|
||||||
|
|
|
|
||||||
LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?;
|
LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::<u16>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::<u16>()`
|
||||||
|
|
||||||
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
||||||
--> tests/ui/from_str_radix_10.rs:39:5
|
--> tests/ui/from_str_radix_10.rs:40:5
|
||||||
|
|
|
|
||||||
LL | i128::from_str_radix(Test + Test, 10)?;
|
LL | i128::from_str_radix(Test + Test, 10)?;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::<i128>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::<i128>()`
|
||||||
|
|
||||||
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
||||||
--> tests/ui/from_str_radix_10.rs:43:5
|
--> tests/ui/from_str_radix_10.rs:44:5
|
||||||
|
|
|
|
||||||
LL | i32::from_str_radix(string, 10)?;
|
LL | i32::from_str_radix(string, 10)?;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::<i32>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::<i32>()`
|
||||||
|
|
||||||
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
error: this call to `from_str_radix` can be replaced with a call to `str::parse`
|
||||||
--> tests/ui/from_str_radix_10.rs:47:5
|
--> tests/ui/from_str_radix_10.rs:48:5
|
||||||
|
|
|
|
||||||
LL | i32::from_str_radix(&stringier, 10)?;
|
LL | i32::from_str_radix(&stringier, 10)?;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::<i32>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::<i32>()`
|
||||||
|
|
|
@ -2,7 +2,7 @@ error: this match arm has an identical body to the `_` wildcard arm
|
||||||
--> tests/ui/match_same_arms.rs:12:9
|
--> tests/ui/match_same_arms.rs:12:9
|
||||||
|
|
|
|
||||||
LL | Abc::A => 0,
|
LL | Abc::A => 0,
|
||||||
| ^^^^^^^^^^^ help: try removing the arm
|
| ^^^^^^^^^^^^^ help: try removing the arm
|
||||||
|
|
|
|
||||||
= help: or try changing either arm body
|
= help: or try changing either arm body
|
||||||
note: `_` wildcard arm here
|
note: `_` wildcard arm here
|
||||||
|
@ -17,106 +17,114 @@ error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms.rs:18:9
|
--> tests/ui/match_same_arms.rs:18:9
|
||||||
|
|
|
|
||||||
LL | (1, .., 3) => 42,
|
LL | (1, .., 3) => 42,
|
||||||
| ----------^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `(1, .., 3) | (.., 3)`
|
|
||||||
|
|
|
|
||||||
= help: or try changing either arm body
|
= help: try changing either arm body
|
||||||
note: other arm here
|
help: or try merging the arm patterns
|
||||||
--> tests/ui/match_same_arms.rs:19:9
|
|
|
||||||
|
LL | (1, .., 3) | (.., 3) => 42,
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - (.., 3) => 42,
|
||||||
|
|
|
|
||||||
LL | (.., 3) => 42,
|
|
||||||
| ^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms.rs:25:9
|
--> tests/ui/match_same_arms.rs:25:9
|
||||||
|
|
|
|
||||||
LL | 51 => 1,
|
LL | 51 => 1,
|
||||||
| --^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `51 | 42`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms.rs:24:9
|
|
||||||
|
|
|
||||||
LL | 42 => 1,
|
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | 51 | 42 => 1,
|
||||||
|
| ~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - 42 => 1,
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms.rs:26:9
|
--> tests/ui/match_same_arms.rs:26:9
|
||||||
|
|
|
|
||||||
LL | 41 => 2,
|
LL | 41 => 2,
|
||||||
| --^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `41 | 52`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms.rs:27:9
|
|
||||||
|
|
|
||||||
LL | 52 => 2,
|
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | 41 | 52 => 2,
|
||||||
|
| ~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - 52 => 2,
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms.rs:33:9
|
--> tests/ui/match_same_arms.rs:33:9
|
||||||
|
|
|
|
||||||
LL | 2 => 2,
|
LL | 2 => 2,
|
||||||
| -^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `2 | 1`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms.rs:32:9
|
|
||||||
|
|
|
||||||
LL | 1 => 2,
|
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | 2 | 1 => 2,
|
||||||
|
| ~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - 1 => 2,
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms.rs:35:9
|
--> tests/ui/match_same_arms.rs:35:9
|
||||||
|
|
|
|
||||||
LL | 3 => 2,
|
LL | 3 => 2,
|
||||||
| -^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `3 | 1`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms.rs:32:9
|
|
||||||
|
|
|
||||||
LL | 1 => 2,
|
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | 3 | 1 => 2,
|
||||||
|
| ~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - 1 => 2,
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms.rs:33:9
|
--> tests/ui/match_same_arms.rs:33:9
|
||||||
|
|
|
|
||||||
LL | 2 => 2,
|
LL | 2 => 2,
|
||||||
| -^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `2 | 3`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms.rs:35:9
|
|
||||||
|
|
|
||||||
LL | 3 => 2,
|
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | 2 | 3 => 2,
|
||||||
|
| ~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - 3 => 2,
|
||||||
|
LL +
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms.rs:52:17
|
--> tests/ui/match_same_arms.rs:52:17
|
||||||
|
|
|
|
||||||
LL | CommandInfo::External { name, .. } => name.to_string(),
|
LL | CommandInfo::External { name, .. } => name.to_string(),
|
||||||
| ----------------------------------^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. }`
|
|
||||||
|
|
|
|
||||||
= help: or try changing either arm body
|
= help: try changing either arm body
|
||||||
note: other arm here
|
help: or try merging the arm patterns
|
||||||
--> tests/ui/match_same_arms.rs:51:17
|
|
|
||||||
|
LL | CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. } => name.to_string(),
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - CommandInfo::BuiltIn { name, .. } => name.to_string(),
|
||||||
|
|
|
|
||||||
LL | CommandInfo::BuiltIn { name, .. } => name.to_string(),
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: aborting due to 8 previous errors
|
error: aborting due to 8 previous errors
|
||||||
|
|
||||||
|
|
241
tests/ui/match_same_arms2.fixed
Normal file
241
tests/ui/match_same_arms2.fixed
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
#![warn(clippy::match_same_arms)]
|
||||||
|
#![allow(
|
||||||
|
clippy::disallowed_names,
|
||||||
|
clippy::diverging_sub_expression,
|
||||||
|
clippy::uninlined_format_args,
|
||||||
|
clippy::match_single_binding,
|
||||||
|
clippy::match_like_matches_macro
|
||||||
|
)]
|
||||||
|
fn bar<T>(_: T) {}
|
||||||
|
fn foo() -> bool {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_same_arms() {
|
||||||
|
let _ = match 42 {
|
||||||
|
_ => {
|
||||||
|
foo();
|
||||||
|
let mut a = 42 + [23].len() as i32;
|
||||||
|
if true {
|
||||||
|
a += 7;
|
||||||
|
}
|
||||||
|
a = -31 - a;
|
||||||
|
a
|
||||||
|
},
|
||||||
|
};
|
||||||
|
//~^^^^^^^^^^^^^^^^^^^ ERROR: this match arm has an identical body to the `_` wildcard arm
|
||||||
|
|
||||||
|
let _ = match 42 {
|
||||||
|
51 | 42 => foo(), //~ ERROR: this match arm has an identical body to another arm
|
||||||
|
_ => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = match Some(42) {
|
||||||
|
None | Some(_) => 24, //~ ERROR: this match arm has an identical body to another arm
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = match Some(42) {
|
||||||
|
Some(foo) => 24,
|
||||||
|
None => 24,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = match Some(42) {
|
||||||
|
Some(42) => 24,
|
||||||
|
Some(a) => 24, // bindings are different
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = match Some(42) {
|
||||||
|
Some(a) if a > 0 => 24,
|
||||||
|
Some(a) => 24, // one arm has a guard
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
match (Some(42), Some(42)) {
|
||||||
|
(None, Some(a)) | (Some(a), None) => bar(a), //~ ERROR: this match arm has an identical body to another arm
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
// No warning because guards are different
|
||||||
|
let _ = match Some(42) {
|
||||||
|
Some(a) if a == 42 => a,
|
||||||
|
Some(a) if a == 24 => a,
|
||||||
|
Some(_) => 24,
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = match (Some(42), Some(42)) {
|
||||||
|
(None, Some(a)) | (Some(a), None) if a == 42 => a, //~ ERROR: this match arm has an identical body to another arm
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
match (Some(42), Some(42)) {
|
||||||
|
(Some(a), ..) | (.., Some(a)) => bar(a), //~ ERROR: this match arm has an identical body to another arm
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = match Some(()) {
|
||||||
|
Some(()) => 0.0,
|
||||||
|
None => -0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
match (Some(42), Some("")) {
|
||||||
|
(Some(a), None) => bar(a),
|
||||||
|
(None, Some(a)) => bar(a), // bindings have different types
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
let x: Result<i32, &str> = Ok(3);
|
||||||
|
|
||||||
|
// No warning because of the guard.
|
||||||
|
match x {
|
||||||
|
Ok(x) if x * x == 64 => println!("ok"),
|
||||||
|
Ok(_) => println!("ok"),
|
||||||
|
Err(_) => println!("err"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// This used to be a false positive; see issue #1996.
|
||||||
|
match x {
|
||||||
|
Ok(3) => println!("ok"),
|
||||||
|
Ok(x) if x * x == 64 => println!("ok 64"),
|
||||||
|
Ok(_) => println!("ok"),
|
||||||
|
Err(_) => println!("err"),
|
||||||
|
}
|
||||||
|
|
||||||
|
match (x, Some(1i32)) {
|
||||||
|
(Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), //~ ERROR: this match arm has an identical body to another arm
|
||||||
|
_ => println!("err"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// No warning; different types for `x`.
|
||||||
|
match (x, Some(1.0f64)) {
|
||||||
|
(Ok(x), Some(_)) => println!("ok {}", x),
|
||||||
|
(Ok(_), Some(x)) => println!("ok {}", x),
|
||||||
|
_ => println!("err"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// False negative #2251.
|
||||||
|
match x {
|
||||||
|
Ok(_tmp) => println!("ok"),
|
||||||
|
Ok(_) | Ok(3) => println!("ok"), //~ ERROR: this match arm has an identical body to another arm
|
||||||
|
Err(_) => {
|
||||||
|
unreachable!();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// False positive #1390
|
||||||
|
macro_rules! empty {
|
||||||
|
($e:expr) => {};
|
||||||
|
}
|
||||||
|
match 0 {
|
||||||
|
0 => {
|
||||||
|
empty!(0);
|
||||||
|
},
|
||||||
|
1 => {
|
||||||
|
empty!(1);
|
||||||
|
},
|
||||||
|
x => {
|
||||||
|
empty!(x);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// still lint if the tokens are the same
|
||||||
|
match 0 {
|
||||||
|
1 | 0 => {
|
||||||
|
empty!(0);
|
||||||
|
},
|
||||||
|
x => {
|
||||||
|
empty!(x);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
//~^^^^^^^ ERROR: this match arm has an identical body to another arm
|
||||||
|
|
||||||
|
match_expr_like_matches_macro_priority();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_expr_like_matches_macro_priority() {
|
||||||
|
enum E {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
}
|
||||||
|
let x = E::A;
|
||||||
|
let _ans = match x {
|
||||||
|
E::A => false,
|
||||||
|
E::B => false,
|
||||||
|
_ => true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = match Some(0) {
|
||||||
|
Some(0) => 0,
|
||||||
|
Some(1) => 1,
|
||||||
|
#[cfg(feature = "foo")]
|
||||||
|
Some(2) => 2,
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Foo {
|
||||||
|
X(u32),
|
||||||
|
Y(u32),
|
||||||
|
Z(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between.
|
||||||
|
let _ = match Foo::X(0) {
|
||||||
|
Foo::X(0) => 1,
|
||||||
|
Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2,
|
||||||
|
Foo::Z(_) => 1,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Suggest moving `Foo::Z(_)` up.
|
||||||
|
let _ = match Foo::X(0) {
|
||||||
|
Foo::X(0) | Foo::Z(_) => 1, //~ ERROR: this match arm has an identical body to another arm
|
||||||
|
Foo::X(_) | Foo::Y(_) => 2,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Suggest moving `Foo::X(0)` down.
|
||||||
|
let _ = match Foo::X(0) {
|
||||||
|
Foo::Y(_) | Foo::Z(0) => 2,
|
||||||
|
Foo::Z(_) | Foo::X(0) => 1, //~ ERROR: this match arm has an identical body to another arm
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't lint.
|
||||||
|
let _ = match 0 {
|
||||||
|
-2 => 1,
|
||||||
|
-5..=50 => 2,
|
||||||
|
-150..=88 => 1,
|
||||||
|
_ => 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Bar {
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
z: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lint.
|
||||||
|
let _ = match None {
|
||||||
|
Some(Bar { y: 10, z: 0, .. }) => 2,
|
||||||
|
None => 50,
|
||||||
|
Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, //~ ERROR: this match arm has an identical body to another arm
|
||||||
|
_ => 200,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = match 0 {
|
||||||
|
0 => todo!(),
|
||||||
|
1 => todo!(),
|
||||||
|
2 => core::convert::identity::<u32>(todo!()),
|
||||||
|
3 => core::convert::identity::<u32>(todo!()),
|
||||||
|
_ => 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = match 0 {
|
||||||
|
1 | 0 => cfg!(not_enable),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
|
@ -2,9 +2,10 @@
|
||||||
#![allow(
|
#![allow(
|
||||||
clippy::disallowed_names,
|
clippy::disallowed_names,
|
||||||
clippy::diverging_sub_expression,
|
clippy::diverging_sub_expression,
|
||||||
clippy::uninlined_format_args
|
clippy::uninlined_format_args,
|
||||||
|
clippy::match_single_binding,
|
||||||
|
clippy::match_like_matches_macro
|
||||||
)]
|
)]
|
||||||
//@no-rustfix
|
|
||||||
fn bar<T>(_: T) {}
|
fn bar<T>(_: T) {}
|
||||||
fn foo() -> bool {
|
fn foo() -> bool {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
error: this match arm has an identical body to the `_` wildcard arm
|
error: this match arm has an identical body to the `_` wildcard arm
|
||||||
--> tests/ui/match_same_arms2.rs:15:9
|
--> tests/ui/match_same_arms2.rs:16:9
|
||||||
|
|
|
|
||||||
LL | / 42 => {
|
LL | / 42 => {
|
||||||
LL | | foo();
|
LL | | foo();
|
||||||
LL | | let mut a = 42 + [23].len() as i32;
|
LL | | let mut a = 42 + [23].len() as i32;
|
||||||
LL | | if true {
|
LL | | if true {
|
||||||
... |
|
... |
|
||||||
LL | | a
|
|
||||||
LL | | },
|
LL | | },
|
||||||
| |_________^ help: try removing the arm
|
LL | | _ => {
|
||||||
|
| |________^ help: try removing the arm
|
||||||
|
|
|
|
||||||
= help: or try changing either arm body
|
= help: or try changing either arm body
|
||||||
note: `_` wildcard arm here
|
note: `_` wildcard arm here
|
||||||
--> tests/ui/match_same_arms2.rs:24:9
|
--> tests/ui/match_same_arms2.rs:25:9
|
||||||
|
|
|
|
||||||
LL | / _ => {
|
LL | / _ => {
|
||||||
LL | | foo();
|
LL | | foo();
|
||||||
|
@ -26,203 +26,200 @@ LL | | },
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
|
= help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms2.rs:38:9
|
--> tests/ui/match_same_arms2.rs:39:9
|
||||||
|
|
|
|
||||||
LL | 51 => foo(),
|
LL | 51 => foo(),
|
||||||
| --^^^^^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `51 | 42`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms2.rs:37:9
|
|
||||||
|
|
|
||||||
LL | 42 => foo(),
|
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | 51 | 42 => foo(),
|
||||||
|
| ~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - 42 => foo(),
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms2.rs:44:9
|
--> tests/ui/match_same_arms2.rs:45:9
|
||||||
|
|
|
|
||||||
LL | None => 24,
|
LL | None => 24,
|
||||||
| ----^^^^^^
|
| ^^^^^^^^^^
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `None | Some(_)`
|
|
||||||
|
|
|
|
||||||
= help: or try changing either arm body
|
= help: try changing either arm body
|
||||||
note: other arm here
|
help: or try merging the arm patterns
|
||||||
--> tests/ui/match_same_arms2.rs:43:9
|
|
|
||||||
|
LL | None | Some(_) => 24,
|
||||||
|
| ~~~~~~~~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - Some(_) => 24,
|
||||||
|
|
|
|
||||||
LL | Some(_) => 24,
|
|
||||||
| ^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms2.rs:66:9
|
--> tests/ui/match_same_arms2.rs:67:9
|
||||||
|
|
|
|
||||||
LL | (None, Some(a)) => bar(a),
|
LL | (None, Some(a)) => bar(a),
|
||||||
| ---------------^^^^^^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms2.rs:65:9
|
|
||||||
|
|
|
||||||
LL | (Some(a), None) => bar(a),
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | (None, Some(a)) | (Some(a), None) => bar(a),
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - (Some(a), None) => bar(a),
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms2.rs:80:9
|
--> tests/ui/match_same_arms2.rs:81:9
|
||||||
|
|
|
|
||||||
LL | (None, Some(a)) if a == 42 => a,
|
LL | (None, Some(a)) if a == 42 => a,
|
||||||
| ---------------^^^^^^^^^^^^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms2.rs:79:9
|
|
||||||
|
|
|
||||||
LL | (Some(a), None) if a == 42 => a,
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | (None, Some(a)) | (Some(a), None) if a == 42 => a,
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - (Some(a), None) if a == 42 => a,
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms2.rs:85:9
|
|
||||||
|
|
|
||||||
LL | (Some(a), ..) => bar(a),
|
|
||||||
| -------------^^^^^^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `(Some(a), ..) | (.., Some(a))`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms2.rs:86:9
|
--> tests/ui/match_same_arms2.rs:86:9
|
||||||
|
|
|
|
||||||
LL | (.., Some(a)) => bar(a),
|
LL | (Some(a), ..) => bar(a),
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | (Some(a), ..) | (.., Some(a)) => bar(a),
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - (.., Some(a)) => bar(a),
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms2.rs:119:9
|
|
||||||
|
|
|
||||||
LL | (Ok(x), Some(_)) => println!("ok {}", x),
|
|
||||||
| ----------------^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `(Ok(x), Some(_)) | (Ok(_), Some(x))`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms2.rs:120:9
|
--> tests/ui/match_same_arms2.rs:120:9
|
||||||
|
|
|
|
||||||
LL | (Ok(_), Some(x)) => println!("ok {}", x),
|
LL | (Ok(x), Some(_)) => println!("ok {}", x),
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x),
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - (Ok(_), Some(x)) => println!("ok {}", x),
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms2.rs:135:9
|
--> tests/ui/match_same_arms2.rs:136:9
|
||||||
|
|
|
|
||||||
LL | Ok(_) => println!("ok"),
|
LL | Ok(_) => println!("ok"),
|
||||||
| -----^^^^^^^^^^^^^^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `Ok(_) | Ok(3)`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms2.rs:134:9
|
|
||||||
|
|
|
||||||
LL | Ok(3) => println!("ok"),
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | Ok(_) | Ok(3) => println!("ok"),
|
||||||
|
| ~~~~~~~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - Ok(3) => println!("ok"),
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms2.rs:162:9
|
--> tests/ui/match_same_arms2.rs:163:9
|
||||||
|
|
|
|
||||||
LL | 1 => {
|
LL | / 1 => {
|
||||||
| ^ help: try merging the arm patterns: `1 | 0`
|
|
||||||
| _________|
|
|
||||||
| |
|
|
||||||
LL | | empty!(0);
|
LL | | empty!(0);
|
||||||
LL | | },
|
LL | | },
|
||||||
| |_________^
|
| |_________^
|
||||||
|
|
|
|
||||||
= help: or try changing either arm body
|
= help: try changing either arm body
|
||||||
note: other arm here
|
help: or try merging the arm patterns
|
||||||
--> tests/ui/match_same_arms2.rs:159:9
|
|
||||||
|
|
|
|
||||||
LL | / 0 => {
|
LL | 1 | 0 => {
|
||||||
LL | | empty!(0);
|
| ~~~~~
|
||||||
LL | | },
|
help: and remove this obsolete arm
|
||||||
| |_________^
|
|
||||||
|
|
||||||
error: match expression looks like `matches!` macro
|
|
||||||
--> tests/ui/match_same_arms2.rs:181:16
|
|
||||||
|
|
|
|
||||||
LL | let _ans = match x {
|
LL - 0 => {
|
||||||
| ________________^
|
LL - empty!(0);
|
||||||
LL | | E::A => false,
|
LL - },
|
||||||
LL | | E::B => false,
|
|
||||||
LL | | _ => true,
|
|
||||||
LL | | };
|
|
||||||
| |_____^ help: try: `!matches!(x, E::A | E::B)`
|
|
||||||
|
|
|
|
||||||
= note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
|
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]`
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms2.rs:213:9
|
--> tests/ui/match_same_arms2.rs:214:9
|
||||||
|
|
|
||||||
LL | Foo::X(0) => 1,
|
|
||||||
| ---------^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `Foo::X(0) | Foo::Z(_)`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms2.rs:215:9
|
|
||||||
|
|
|
||||||
LL | Foo::Z(_) => 1,
|
|
||||||
| ^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
|
||||||
--> tests/ui/match_same_arms2.rs:223:9
|
|
||||||
|
|
|
||||||
LL | Foo::Z(_) => 1,
|
|
||||||
| ---------^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `Foo::Z(_) | Foo::X(0)`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms2.rs:221:9
|
|
||||||
|
|
|
|
||||||
LL | Foo::X(0) => 1,
|
LL | Foo::X(0) => 1,
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | Foo::X(0) | Foo::Z(_) => 1,
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - Foo::Z(_) => 1,
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms2.rs:246:9
|
--> tests/ui/match_same_arms2.rs:224:9
|
||||||
|
|
|
||||||
|
LL | Foo::Z(_) => 1,
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | Foo::Z(_) | Foo::X(0) => 1,
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - Foo::X(0) => 1,
|
||||||
|
|
|
||||||
|
|
||||||
|
error: this match arm has an identical body to another arm
|
||||||
|
--> tests/ui/match_same_arms2.rs:247:9
|
||||||
|
|
|
|
||||||
LL | Some(Bar { y: 0, x: 5, .. }) => 1,
|
LL | Some(Bar { y: 0, x: 5, .. }) => 1,
|
||||||
| ----------------------------^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. })`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms2.rs:243:9
|
|
||||||
|
|
|
||||||
LL | Some(Bar { x: 0, y: 5, .. }) => 1,
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1,
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - Some(Bar { x: 0, y: 5, .. }) => 1,
|
||||||
|
|
|
||||||
|
|
||||||
error: this match arm has an identical body to another arm
|
error: this match arm has an identical body to another arm
|
||||||
--> tests/ui/match_same_arms2.rs:260:9
|
--> tests/ui/match_same_arms2.rs:261:9
|
||||||
|
|
|
|
||||||
LL | 1 => cfg!(not_enable),
|
LL | 1 => cfg!(not_enable),
|
||||||
| -^^^^^^^^^^^^^^^^^^^^
|
|
||||||
| |
|
|
||||||
| help: try merging the arm patterns: `1 | 0`
|
|
||||||
|
|
|
||||||
= help: or try changing either arm body
|
|
||||||
note: other arm here
|
|
||||||
--> tests/ui/match_same_arms2.rs:259:9
|
|
||||||
|
|
|
||||||
LL | 0 => cfg!(not_enable),
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: try changing either arm body
|
||||||
|
help: or try merging the arm patterns
|
||||||
|
|
|
||||||
|
LL | 1 | 0 => cfg!(not_enable),
|
||||||
|
| ~~~~~
|
||||||
|
help: and remove this obsolete arm
|
||||||
|
|
|
||||||
|
LL - 0 => cfg!(not_enable),
|
||||||
|
|
|
||||||
|
|
||||||
error: aborting due to 14 previous errors
|
error: aborting due to 13 previous errors
|
||||||
|
|
||||||
|
|
61
tests/ui/match_same_arms_non_exhaustive.fixed
Normal file
61
tests/ui/match_same_arms_non_exhaustive.fixed
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#![feature(non_exhaustive_omitted_patterns_lint)]
|
||||||
|
#![warn(clippy::match_same_arms)]
|
||||||
|
#![no_main]
|
||||||
|
use std::sync::atomic::Ordering; // #[non_exhaustive] enum
|
||||||
|
|
||||||
|
fn repeat() -> ! {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn f(x: Ordering) {
|
||||||
|
#[deny(non_exhaustive_omitted_patterns)]
|
||||||
|
match x {
|
||||||
|
Ordering::Relaxed => println!("relaxed"),
|
||||||
|
Ordering::Release => println!("release"),
|
||||||
|
Ordering::Acquire => println!("acquire"),
|
||||||
|
Ordering::AcqRel | Ordering::SeqCst => repeat(),
|
||||||
|
_ => repeat(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod f {
|
||||||
|
#![deny(non_exhaustive_omitted_patterns)]
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn f(x: Ordering) {
|
||||||
|
match x {
|
||||||
|
Ordering::Relaxed => println!("relaxed"),
|
||||||
|
Ordering::Release => println!("release"),
|
||||||
|
Ordering::Acquire => println!("acquire"),
|
||||||
|
Ordering::AcqRel | Ordering::SeqCst => repeat(),
|
||||||
|
_ => repeat(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Below should still lint
|
||||||
|
|
||||||
|
pub fn g(x: Ordering) {
|
||||||
|
match x {
|
||||||
|
Ordering::Relaxed => println!("relaxed"),
|
||||||
|
Ordering::Release => println!("release"),
|
||||||
|
Ordering::Acquire => println!("acquire"),
|
||||||
|
//~^ ERROR: this match arm has an identical body to the `_` wildcard arm
|
||||||
|
_ => repeat(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod g {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn g(x: Ordering) {
|
||||||
|
match x {
|
||||||
|
Ordering::Relaxed => println!("relaxed"),
|
||||||
|
Ordering::Release => println!("release"),
|
||||||
|
Ordering::Acquire => println!("acquire"),
|
||||||
|
//~^ ERROR: this match arm has an identical body to the `_` wildcard arm
|
||||||
|
_ => repeat(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
#![feature(non_exhaustive_omitted_patterns_lint)]
|
#![feature(non_exhaustive_omitted_patterns_lint)]
|
||||||
#![warn(clippy::match_same_arms)]
|
#![warn(clippy::match_same_arms)]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
//@no-rustfix
|
|
||||||
use std::sync::atomic::Ordering; // #[non_exhaustive] enum
|
use std::sync::atomic::Ordering; // #[non_exhaustive] enum
|
||||||
|
|
||||||
fn repeat() -> ! {
|
fn repeat() -> ! {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
error: this match arm has an identical body to the `_` wildcard arm
|
error: this match arm has an identical body to the `_` wildcard arm
|
||||||
--> tests/ui/match_same_arms_non_exhaustive.rs:45:9
|
--> tests/ui/match_same_arms_non_exhaustive.rs:44:9
|
||||||
|
|
|
|
||||||
LL | Ordering::AcqRel | Ordering::SeqCst => repeat(),
|
LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(),
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm
|
LL | |
|
||||||
|
| |________^ help: try removing the arm
|
||||||
|
|
|
|
||||||
= help: or try changing either arm body
|
= help: or try changing either arm body
|
||||||
note: `_` wildcard arm here
|
note: `_` wildcard arm here
|
||||||
--> tests/ui/match_same_arms_non_exhaustive.rs:47:9
|
--> tests/ui/match_same_arms_non_exhaustive.rs:46:9
|
||||||
|
|
|
|
||||||
LL | _ => repeat(),
|
LL | _ => repeat(),
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
@ -14,14 +15,15 @@ LL | _ => repeat(),
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
|
= help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
|
||||||
|
|
||||||
error: this match arm has an identical body to the `_` wildcard arm
|
error: this match arm has an identical body to the `_` wildcard arm
|
||||||
--> tests/ui/match_same_arms_non_exhaustive.rs:59:13
|
--> tests/ui/match_same_arms_non_exhaustive.rs:58:13
|
||||||
|
|
|
|
||||||
LL | Ordering::AcqRel | Ordering::SeqCst => repeat(),
|
LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(),
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm
|
LL | |
|
||||||
|
| |____________^ help: try removing the arm
|
||||||
|
|
|
|
||||||
= help: or try changing either arm body
|
= help: or try changing either arm body
|
||||||
note: `_` wildcard arm here
|
note: `_` wildcard arm here
|
||||||
--> tests/ui/match_same_arms_non_exhaustive.rs:61:13
|
--> tests/ui/match_same_arms_non_exhaustive.rs:60:13
|
||||||
|
|
|
|
||||||
LL | _ => repeat(),
|
LL | _ => repeat(),
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue