mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-01 11:13:43 +00:00
Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
c200dad300
@ -5249,6 +5249,7 @@ Released 2018-09-13
|
||||
[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
|
||||
[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
|
||||
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
|
||||
[`doc_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_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
|
||||
@ -5447,6 +5448,7 @@ Released 2018-09-13
|
||||
[`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
|
||||
[`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
|
||||
[`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
|
||||
@ -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_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
|
||||
[`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_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
|
||||
@ -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-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-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-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
|
||||
@ -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
|
||||
[`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-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 -->
|
||||
|
@ -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)
|
||||
|
||||
|
||||
## `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`
|
||||
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)
|
||||
|
||||
|
||||
## `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_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_TRAITS_WITH_RENAMED_PARAMS: &[&str] =
|
||||
&["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"];
|
||||
|
||||
/// Conf with parse errors
|
||||
#[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
|
||||
/// 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()),
|
||||
/// 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.
|
||||
@ -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.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.allow_renamed_params_for,
|
||||
DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS,
|
||||
);
|
||||
// TODO: THIS SHOULD BE TESTED, this comment will be gone soon
|
||||
if conf.conf.allowed_idents_below_min_chars.contains("..") {
|
||||
conf.conf
|
||||
|
@ -26,7 +26,8 @@ msrv_aliases! {
|
||||
1,63,0 { CLONE_INTO }
|
||||
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
|
||||
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,54,0 { INTO_KEYS }
|
||||
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
|
||||
|
@ -1,11 +1,12 @@
|
||||
[package]
|
||||
name = "clippy_dev"
|
||||
description = "Clippy developer tooling"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
aho-corasick = "1.0"
|
||||
clap = "4.1.4"
|
||||
clap = { version = "4.4", features = ["derive"] }
|
||||
indoc = "1.0"
|
||||
itertools = "0.12"
|
||||
opener = "0.6"
|
||||
|
@ -2,350 +2,292 @@
|
||||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||
#![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 indoc::indoc;
|
||||
use std::convert::Infallible;
|
||||
|
||||
fn main() {
|
||||
let matches = get_clap_config();
|
||||
let dev = Dev::parse();
|
||||
|
||||
match matches.subcommand() {
|
||||
Some(("bless", _)) => {
|
||||
match dev.command {
|
||||
DevCommand::Bless => {
|
||||
eprintln!("use `cargo bless` to automatically replace `.stderr` and `.fixed` files as tests are being run");
|
||||
},
|
||||
Some(("dogfood", matches)) => {
|
||||
dogfood::dogfood(
|
||||
matches.get_flag("fix"),
|
||||
matches.get_flag("allow-dirty"),
|
||||
matches.get_flag("allow-staged"),
|
||||
);
|
||||
},
|
||||
Some(("fmt", matches)) => {
|
||||
fmt::run(matches.get_flag("check"), matches.get_flag("verbose"));
|
||||
},
|
||||
Some(("update_lints", matches)) => {
|
||||
if matches.get_flag("print-only") {
|
||||
DevCommand::Dogfood {
|
||||
fix,
|
||||
allow_dirty,
|
||||
allow_staged,
|
||||
} => dogfood::dogfood(fix, allow_dirty, allow_staged),
|
||||
DevCommand::Fmt { check, verbose } => fmt::run(check, verbose),
|
||||
DevCommand::UpdateLints { print_only, check } => {
|
||||
if print_only {
|
||||
update_lints::print_lints();
|
||||
} else if matches.get_flag("check") {
|
||||
} else if check {
|
||||
update_lints::update(update_lints::UpdateMode::Check);
|
||||
} else {
|
||||
update_lints::update(update_lints::UpdateMode::Change);
|
||||
}
|
||||
},
|
||||
Some(("new_lint", matches)) => {
|
||||
match new_lint::create(
|
||||
matches.get_one::<String>("pass").unwrap(),
|
||||
matches.get_one::<String>("name"),
|
||||
matches.get_one::<String>("category").map(String::as_str),
|
||||
matches.get_one::<String>("type").map(String::as_str),
|
||||
matches.get_flag("msrv"),
|
||||
) {
|
||||
Ok(()) => update_lints::update(update_lints::UpdateMode::Change),
|
||||
Err(e) => eprintln!("Unable to create lint: {e}"),
|
||||
}
|
||||
DevCommand::NewLint {
|
||||
pass,
|
||||
name,
|
||||
category,
|
||||
r#type,
|
||||
msrv,
|
||||
} => match new_lint::create(&pass, &name, &category, r#type.as_deref(), msrv) {
|
||||
Ok(()) => update_lints::update(update_lints::UpdateMode::Change),
|
||||
Err(e) => eprintln!("Unable to create lint: {e}"),
|
||||
},
|
||||
Some(("setup", sub_command)) => match sub_command.subcommand() {
|
||||
Some(("git-hook", matches)) => {
|
||||
if matches.get_flag("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") {
|
||||
DevCommand::Setup(SetupCommand { subcommand }) => match subcommand {
|
||||
SetupSubcommand::Intellij { remove, repo_path } => {
|
||||
if remove {
|
||||
setup::intellij::remove_rustc_src();
|
||||
} else {
|
||||
setup::intellij::setup_rustc_src(
|
||||
matches
|
||||
.get_one::<String>("rustc-repo-path")
|
||||
.expect("this field is mandatory and therefore always valid"),
|
||||
);
|
||||
setup::intellij::setup_rustc_src(&repo_path);
|
||||
}
|
||||
},
|
||||
Some(("toolchain", matches)) => {
|
||||
setup::toolchain::create(
|
||||
matches.get_flag("force"),
|
||||
matches.get_flag("release"),
|
||||
matches.get_one::<String>("name").unwrap(),
|
||||
);
|
||||
SetupSubcommand::GitHook { remove, force_override } => {
|
||||
if remove {
|
||||
setup::git_hook::remove_hook();
|
||||
} else {
|
||||
setup::git_hook::install_hook(force_override);
|
||||
}
|
||||
},
|
||||
Some(("vscode-tasks", matches)) => {
|
||||
if matches.get_flag("remove") {
|
||||
SetupSubcommand::Toolchain { force, release, name } => setup::toolchain::create(force, release, &name),
|
||||
SetupSubcommand::VscodeTasks { remove, force_override } => {
|
||||
if remove {
|
||||
setup::vscode::remove_tasks();
|
||||
} else {
|
||||
setup::vscode::install_tasks(matches.get_flag("force-override"));
|
||||
setup::vscode::install_tasks(force_override);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
Some(("remove", sub_command)) => match sub_command.subcommand() {
|
||||
Some(("git-hook", _)) => setup::git_hook::remove_hook(),
|
||||
Some(("intellij", _)) => setup::intellij::remove_rustc_src(),
|
||||
Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(),
|
||||
_ => {},
|
||||
DevCommand::Remove(RemoveCommand { subcommand }) => match subcommand {
|
||||
RemoveSubcommand::Intellij => setup::intellij::remove_rustc_src(),
|
||||
RemoveSubcommand::GitHook => setup::git_hook::remove_hook(),
|
||||
RemoveSubcommand::VscodeTasks => setup::vscode::remove_tasks(),
|
||||
},
|
||||
Some(("serve", matches)) => {
|
||||
let port = *matches.get_one::<u16>("port").unwrap();
|
||||
let lint = matches.get_one::<String>("lint");
|
||||
serve::run(port, lint);
|
||||
},
|
||||
Some(("lint", matches)) => {
|
||||
let path = matches.get_one::<String>("path").unwrap();
|
||||
let args = matches.get_many::<String>("args").into_iter().flatten();
|
||||
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);
|
||||
},
|
||||
_ => {},
|
||||
DevCommand::Serve { port, lint } => serve::run(port, lint),
|
||||
DevCommand::Lint { path, args } => lint::run(&path, args.iter()),
|
||||
DevCommand::RenameLint {
|
||||
old_name,
|
||||
new_name,
|
||||
uplift,
|
||||
} => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift),
|
||||
DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, reason.as_deref()),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_clap_config() -> ArgMatches {
|
||||
Command::new("Clippy developer tooling")
|
||||
.arg_required_else_help(true)
|
||||
.subcommands([
|
||||
Command::new("bless").about("bless the test output changes").arg(
|
||||
Arg::new("ignore-timestamp")
|
||||
.long("ignore-timestamp")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Include files updated before clippy was built"),
|
||||
),
|
||||
Command::new("dogfood").about("Runs the dogfood test").args([
|
||||
Arg::new("fix")
|
||||
.long("fix")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Apply the suggestions when possible"),
|
||||
Arg::new("allow-dirty")
|
||||
.long("allow-dirty")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Fix code even if the working directory has changes")
|
||||
.requires("fix"),
|
||||
Arg::new("allow-staged")
|
||||
.long("allow-staged")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Fix code even if the working directory has staged changes")
|
||||
.requires("fix"),
|
||||
]),
|
||||
Command::new("fmt")
|
||||
.about("Run rustfmt on all projects and tests")
|
||||
.args([
|
||||
Arg::new("check")
|
||||
.long("check")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Use the rustfmt --check option"),
|
||||
Arg::new("verbose")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Echo commands run"),
|
||||
]),
|
||||
Command::new("update_lints")
|
||||
.about("Updates lint registration and information from the source code")
|
||||
.long_about(
|
||||
"Makes sure that:\n \
|
||||
* the lint count in README.md is correct\n \
|
||||
* the changelog contains markdown link references at the bottom\n \
|
||||
* all lint groups include the correct lints\n \
|
||||
* lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
|
||||
* all lints are registered in the lint store",
|
||||
)
|
||||
.args([
|
||||
Arg::new("print-only")
|
||||
.long("print-only")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help(
|
||||
"Print a table of lints to STDOUT. \
|
||||
This does not include deprecated and internal lints. \
|
||||
(Does not modify any files)",
|
||||
),
|
||||
Arg::new("check")
|
||||
.long("check")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Checks that `cargo dev update_lints` has been run. Used on CI."),
|
||||
]),
|
||||
Command::new("new_lint")
|
||||
.about("Create new lint and run `cargo dev update_lints`")
|
||||
.args([
|
||||
Arg::new("pass")
|
||||
.short('p')
|
||||
.long("pass")
|
||||
.help("Specify whether the lint runs during the early or late pass")
|
||||
.value_parser(["early", "late"])
|
||||
.conflicts_with("type")
|
||||
.default_value("late"),
|
||||
Arg::new("name")
|
||||
.short('n')
|
||||
.long("name")
|
||||
.help("Name of the new lint in snake case, ex: fn_too_long")
|
||||
.required(true)
|
||||
.value_parser(|name: &str| Ok::<_, Infallible>(name.replace('-', "_"))),
|
||||
Arg::new("category")
|
||||
.short('c')
|
||||
.long("category")
|
||||
.help("What category the lint belongs to")
|
||||
.default_value("nursery")
|
||||
.value_parser([
|
||||
"style",
|
||||
"correctness",
|
||||
"suspicious",
|
||||
"complexity",
|
||||
"perf",
|
||||
"pedantic",
|
||||
"restriction",
|
||||
"cargo",
|
||||
"nursery",
|
||||
"internal",
|
||||
]),
|
||||
Arg::new("type").long("type").help("What directory the lint belongs in"),
|
||||
Arg::new("msrv")
|
||||
.long("msrv")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Add MSRV config code to the lint"),
|
||||
]),
|
||||
Command::new("setup")
|
||||
.about("Support for setting up your personal development environment")
|
||||
.arg_required_else_help(true)
|
||||
.subcommands([
|
||||
Command::new("git-hook")
|
||||
.about("Add a pre-commit git hook that formats your code to make it look pretty")
|
||||
.args([
|
||||
Arg::new("remove")
|
||||
.long("remove")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"),
|
||||
Arg::new("force-override")
|
||||
.long("force-override")
|
||||
.short('f')
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Forces the override of an existing git pre-commit hook"),
|
||||
]),
|
||||
Command::new("intellij")
|
||||
.about("Alter dependencies so Intellij Rust can find rustc internals")
|
||||
.args([
|
||||
Arg::new("remove")
|
||||
.long("remove")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Remove the dependencies added with 'cargo dev setup intellij'"),
|
||||
Arg::new("rustc-repo-path")
|
||||
.long("repo-path")
|
||||
.short('r')
|
||||
.help("The path to a rustc repo that will be used for setting the dependencies")
|
||||
.value_name("path")
|
||||
.conflicts_with("remove")
|
||||
.required(true),
|
||||
]),
|
||||
Command::new("toolchain")
|
||||
.about("Install a rustup toolchain pointing to the local clippy build")
|
||||
.args([
|
||||
Arg::new("force")
|
||||
.long("force")
|
||||
.short('f')
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Override an existing toolchain"),
|
||||
Arg::new("release")
|
||||
.long("release")
|
||||
.short('r')
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Point to --release clippy binaries"),
|
||||
Arg::new("name")
|
||||
.long("name")
|
||||
.default_value("clippy")
|
||||
.help("The name of the created toolchain"),
|
||||
]),
|
||||
Command::new("vscode-tasks")
|
||||
.about("Add several tasks to vscode for formatting, validation and testing")
|
||||
.args([
|
||||
Arg::new("remove")
|
||||
.long("remove")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Remove the tasks added with 'cargo dev setup vscode-tasks'"),
|
||||
Arg::new("force-override")
|
||||
.long("force-override")
|
||||
.short('f')
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Forces the override of existing vscode tasks"),
|
||||
]),
|
||||
]),
|
||||
Command::new("remove")
|
||||
.about("Support for undoing changes done by the setup command")
|
||||
.arg_required_else_help(true)
|
||||
.subcommands([
|
||||
Command::new("git-hook").about("Remove any existing pre-commit git hook"),
|
||||
Command::new("vscode-tasks").about("Remove any existing vscode tasks"),
|
||||
Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"),
|
||||
]),
|
||||
Command::new("serve")
|
||||
.about("Launch a local 'ALL the Clippy Lints' website in a browser")
|
||||
.args([
|
||||
Arg::new("port")
|
||||
.long("port")
|
||||
.short('p')
|
||||
.help("Local port for the http server")
|
||||
.default_value("8000")
|
||||
.value_parser(clap::value_parser!(u16)),
|
||||
Arg::new("lint").help("Which lint's page to load initially (optional)"),
|
||||
]),
|
||||
Command::new("lint")
|
||||
.about("Manually run clippy on a file or package")
|
||||
.after_help(indoc! {"
|
||||
EXAMPLES
|
||||
Lint a single file:
|
||||
cargo dev lint tests/ui/attrs.rs
|
||||
|
||||
Lint a package directory:
|
||||
cargo dev lint tests/ui-cargo/wildcard_dependencies/fail
|
||||
cargo dev lint ~/my-project
|
||||
|
||||
Run rustfix:
|
||||
cargo dev lint ~/my-project -- --fix
|
||||
|
||||
Set lint levels:
|
||||
cargo dev lint file.rs -- -W clippy::pedantic
|
||||
cargo dev lint ~/my-project -- -- -W clippy::pedantic
|
||||
"})
|
||||
.args([
|
||||
Arg::new("path")
|
||||
.required(true)
|
||||
.help("The path to a file or package directory to lint"),
|
||||
Arg::new("args")
|
||||
.action(ArgAction::Append)
|
||||
.help("Pass extra arguments to cargo/clippy-driver"),
|
||||
]),
|
||||
Command::new("rename_lint").about("Renames the given lint").args([
|
||||
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()
|
||||
#[derive(Parser)]
|
||||
#[command(name = "dev", about)]
|
||||
struct Dev {
|
||||
#[command(subcommand)]
|
||||
command: DevCommand,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum DevCommand {
|
||||
/// Bless the test output changes
|
||||
Bless,
|
||||
/// Runs the dogfood test
|
||||
Dogfood {
|
||||
#[arg(long)]
|
||||
/// Apply the suggestions when possible
|
||||
fix: bool,
|
||||
#[arg(long, requires = "fix")]
|
||||
/// Fix code even if the working directory has changes
|
||||
allow_dirty: bool,
|
||||
#[arg(long, requires = "fix")]
|
||||
/// Fix code even if the working directory has staged changes
|
||||
allow_staged: bool,
|
||||
},
|
||||
/// Run rustfmt on all projects and tests
|
||||
Fmt {
|
||||
#[arg(long)]
|
||||
/// Use the rustfmt --check option
|
||||
check: bool,
|
||||
#[arg(short, long)]
|
||||
/// Echo commands run
|
||||
verbose: bool,
|
||||
},
|
||||
#[command(name = "update_lints")]
|
||||
/// Updates lint registration and information from the source code
|
||||
///
|
||||
/// Makes sure that: {n}
|
||||
/// * the lint count in README.md is correct {n}
|
||||
/// * the changelog contains markdown link references at the bottom {n}
|
||||
/// * all lint groups include the correct lints {n}
|
||||
/// * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod` {n}
|
||||
/// * all lints are registered in the lint store
|
||||
UpdateLints {
|
||||
#[arg(long)]
|
||||
/// Print a table of lints to STDOUT
|
||||
///
|
||||
/// This does not include deprecated and internal lints. (Does not modify any files)
|
||||
print_only: bool,
|
||||
#[arg(long)]
|
||||
/// Checks that `cargo dev update_lints` has been run. Used on CI.
|
||||
check: bool,
|
||||
},
|
||||
#[command(name = "new_lint")]
|
||||
/// Create a new lint and run `cargo dev update_lints`
|
||||
NewLint {
|
||||
#[arg(short, long, value_parser = ["early", "late"], conflicts_with = "type", default_value = "late")]
|
||||
/// Specify whether the lint runs during the early or late pass
|
||||
pass: String,
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
value_parser = |name: &str| Ok::<_, Infallible>(name.replace('-', "_")),
|
||||
)]
|
||||
/// Name of the new lint in snake case, ex: `fn_too_long`
|
||||
name: String,
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
value_parser = [
|
||||
"style",
|
||||
"correctness",
|
||||
"suspicious",
|
||||
"complexity",
|
||||
"perf",
|
||||
"pedantic",
|
||||
"restriction",
|
||||
"cargo",
|
||||
"nursery",
|
||||
"internal",
|
||||
],
|
||||
default_value = "nursery",
|
||||
)]
|
||||
/// What category the lint belongs to
|
||||
category: String,
|
||||
#[arg(long)]
|
||||
/// What directory the lint belongs in
|
||||
r#type: Option<String>,
|
||||
#[arg(long)]
|
||||
/// Add MSRV config code to the lint
|
||||
msrv: bool,
|
||||
},
|
||||
/// Support for setting up your personal development environment
|
||||
Setup(SetupCommand),
|
||||
/// Support for removing changes done by the setup command
|
||||
Remove(RemoveCommand),
|
||||
/// Launch a local 'ALL the Clippy Lints' website in a browser
|
||||
Serve {
|
||||
#[arg(short, long, default_value = "8000")]
|
||||
/// Local port for the http server
|
||||
port: u16,
|
||||
#[arg(long)]
|
||||
/// Which lint's page to load initially (optional)
|
||||
lint: Option<String>,
|
||||
},
|
||||
#[allow(clippy::doc_markdown)]
|
||||
/// Manually run clippy on a file or package
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// Lint a single file: {n}
|
||||
/// cargo dev lint tests/ui/attrs.rs
|
||||
///
|
||||
/// Lint a package directory: {n}
|
||||
/// cargo dev lint tests/ui-cargo/wildcard_dependencies/fail {n}
|
||||
/// cargo dev lint ~/my-project
|
||||
///
|
||||
/// Run rustfix: {n}
|
||||
/// cargo dev lint ~/my-project -- --fix
|
||||
///
|
||||
/// Set lint levels: {n}
|
||||
/// cargo dev lint file.rs -- -W clippy::pedantic {n}
|
||||
/// cargo dev lint ~/my-project -- -- -W clippy::pedantic
|
||||
Lint {
|
||||
/// The path to a file or package directory to lint
|
||||
path: String,
|
||||
/// Pass extra arguments to cargo/clippy-driver
|
||||
args: Vec<String>,
|
||||
},
|
||||
#[command(name = "rename_lint")]
|
||||
/// Rename a lint
|
||||
RenameLint {
|
||||
/// The name of the lint to rename
|
||||
old_name: String,
|
||||
#[arg(required_unless_present = "uplift")]
|
||||
/// The new name of the lint
|
||||
new_name: Option<String>,
|
||||
#[arg(long)]
|
||||
/// This lint will be uplifted into rustc
|
||||
uplift: bool,
|
||||
},
|
||||
/// Deprecate the given lint
|
||||
Deprecate {
|
||||
/// The name of the lint to deprecate
|
||||
name: String,
|
||||
#[arg(long, short)]
|
||||
/// The reason for deprecation
|
||||
reason: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
struct SetupCommand {
|
||||
#[command(subcommand)]
|
||||
subcommand: SetupSubcommand,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum SetupSubcommand {
|
||||
/// Alter dependencies so Intellij Rust can find rustc internals
|
||||
Intellij {
|
||||
#[arg(long)]
|
||||
/// Remove the dependencies added with 'cargo dev setup intellij'
|
||||
remove: bool,
|
||||
#[arg(long, short, conflicts_with = "remove")]
|
||||
/// The path to a rustc repo that will be used for setting the dependencies
|
||||
repo_path: String,
|
||||
},
|
||||
/// Add a pre-commit git hook that formats your code to make it look pretty
|
||||
GitHook {
|
||||
#[arg(long)]
|
||||
/// Remove the pre-commit hook added with 'cargo dev setup git-hook'
|
||||
remove: bool,
|
||||
#[arg(long, short)]
|
||||
/// Forces the override of an existing git pre-commit hook
|
||||
force_override: bool,
|
||||
},
|
||||
/// Install a rustup toolchain pointing to the local clippy build
|
||||
Toolchain {
|
||||
#[arg(long, short)]
|
||||
/// Override an existing toolchain
|
||||
force: bool,
|
||||
#[arg(long, short)]
|
||||
/// Point to --release clippy binary
|
||||
release: bool,
|
||||
#[arg(long, default_value = "clippy")]
|
||||
/// Name of the toolchain
|
||||
name: String,
|
||||
},
|
||||
/// Add several tasks to vscode for formatting, validation and testing
|
||||
VscodeTasks {
|
||||
#[arg(long)]
|
||||
/// Remove the tasks added with 'cargo dev setup vscode-tasks'
|
||||
remove: bool,
|
||||
#[arg(long, short)]
|
||||
/// Forces the override of existing vscode tasks
|
||||
force_override: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
struct RemoveCommand {
|
||||
#[command(subcommand)]
|
||||
subcommand: RemoveSubcommand,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum RemoveSubcommand {
|
||||
/// Remove the dependencies added with 'cargo dev setup intellij'
|
||||
Intellij,
|
||||
/// Remove the pre-commit git hook
|
||||
GitHook,
|
||||
/// Remove the tasks added with 'cargo dev setup vscode-tasks'
|
||||
VscodeTasks,
|
||||
}
|
||||
|
@ -36,22 +36,16 @@ impl<T> Context for io::Result<T> {
|
||||
/// # Errors
|
||||
///
|
||||
/// This function errors out if the files couldn't be created or written to.
|
||||
pub fn create(
|
||||
pass: &String,
|
||||
lint_name: Option<&String>,
|
||||
category: Option<&str>,
|
||||
mut ty: Option<&str>,
|
||||
msrv: bool,
|
||||
) -> io::Result<()> {
|
||||
if category == Some("cargo") && ty.is_none() {
|
||||
pub fn create(pass: &str, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> {
|
||||
if category == "cargo" && ty.is_none() {
|
||||
// `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo`
|
||||
ty = Some("cargo");
|
||||
}
|
||||
|
||||
let lint = LintData {
|
||||
pass,
|
||||
name: lint_name.expect("`name` argument is validated by clap"),
|
||||
category: category.expect("`category` argument is validated by clap"),
|
||||
name,
|
||||
category,
|
||||
ty,
|
||||
project_root: clippy_project_root(),
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ use std::{env, thread};
|
||||
/// # Panics
|
||||
///
|
||||
/// 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 {
|
||||
None => format!("http://localhost:{port}"),
|
||||
Some(lint) => format!("http://localhost:{port}/#{lint}"),
|
||||
|
@ -314,7 +314,7 @@ const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
|
||||
/// # Panics
|
||||
///
|
||||
/// 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(
|
||||
(lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
|
||||
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");
|
||||
}
|
||||
|
||||
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_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::macros::HirNode;
|
||||
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_hir::{self as hir, Expr, ExprKind, Node};
|
||||
use rustc_hir::{self as hir, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Instance, Mutability};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::ExpnKind;
|
||||
use rustc_span::{ExpnKind, SyntaxContext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -36,6 +36,7 @@ declare_clippy_lint! {
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct Thing;
|
||||
///
|
||||
/// impl Clone for Thing {
|
||||
/// fn clone(&self) -> Self { todo!() }
|
||||
/// fn clone_from(&mut self, other: &Self) { todo!() }
|
||||
@ -47,7 +48,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub ASSIGNING_CLONES,
|
||||
perf,
|
||||
pedantic,
|
||||
"assigning the result of cloning may be inefficient"
|
||||
}
|
||||
|
||||
@ -67,7 +68,8 @@ impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]);
|
||||
impl<'tcx> LateLintPass<'tcx> for AssigningClones {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx Expr<'_>) {
|
||||
// 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 {
|
||||
ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) | ExpnKind::Macro(..) => return,
|
||||
ExpnKind::Root => {},
|
||||
@ -82,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
|
||||
};
|
||||
|
||||
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.
|
||||
// That is overly conservative - the lint should fire even if there was no initializer,
|
||||
// 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))
|
||||
&& local.init.is_none()
|
||||
{
|
||||
if !local_is_initialized(cx, local) {
|
||||
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)
|
||||
}
|
||||
|
||||
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| {
|
||||
let mut applicability = Applicability::Unspecified;
|
||||
|
||||
diag.span_suggestion(
|
||||
assign_expr.span,
|
||||
call.suggestion_msg(),
|
||||
call.suggested_replacement(cx, lhs, &mut applicability),
|
||||
call.suggested_replacement(cx, ctxt, lhs, &mut applicability),
|
||||
applicability,
|
||||
);
|
||||
});
|
||||
@ -275,6 +281,7 @@ impl<'tcx> CallCandidate<'tcx> {
|
||||
fn suggested_replacement(
|
||||
&self,
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
lhs: &Expr<'tcx>,
|
||||
applicability: &mut Applicability,
|
||||
) -> String {
|
||||
@ -294,7 +301,7 @@ impl<'tcx> CallCandidate<'tcx> {
|
||||
// Determine whether we need to reference the argument to clone_from().
|
||||
let clone_receiver_type = cx.typeck_results().expr_ty(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 {
|
||||
// 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.
|
||||
@ -312,7 +319,7 @@ impl<'tcx> CallCandidate<'tcx> {
|
||||
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.
|
||||
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})")
|
||||
},
|
||||
@ -341,11 +348,11 @@ impl<'tcx> CallCandidate<'tcx> {
|
||||
|
||||
match self.kind {
|
||||
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})")
|
||||
},
|
||||
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})")
|
||||
},
|
||||
}
|
||||
|
@ -36,9 +36,10 @@ fn check_duplicated_attr(
|
||||
}
|
||||
let Some(ident) = attr.ident() else { return };
|
||||
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
|
||||
// conditions are the same.
|
||||
// `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected.
|
||||
return;
|
||||
}
|
||||
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.
|
||||
/// For `use` items these lints are:
|
||||
/// * ambiguous_glob_reexports
|
||||
/// * dead_code
|
||||
/// * deprecated
|
||||
/// * hidden_glob_reexports
|
||||
/// * unreachable_pub
|
||||
/// * unused_imports
|
||||
/// * unused
|
||||
/// * unused_braces
|
||||
/// * unused_import_braces
|
||||
/// * clippy::disallowed_types
|
||||
/// * clippy::enum_glob_use
|
||||
/// * 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
|
||||
///
|
||||
/// 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 clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{first_line_of_span, snippet_opt};
|
||||
use rustc_ast::NestedMetaItem;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
@ -20,26 +21,40 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
|
||||
for lint in lint_list {
|
||||
match item.kind {
|
||||
ItemKind::Use(..) => {
|
||||
if is_word(lint, sym::unused_imports)
|
||||
|| is_word(lint, sym::deprecated)
|
||||
|| is_word(lint, sym!(unreachable_pub))
|
||||
|| is_word(lint, sym!(unused))
|
||||
|| is_word(lint, sym!(unused_import_braces))
|
||||
|| extract_clippy_lint(lint).map_or(false, |s| {
|
||||
matches!(
|
||||
s.as_str(),
|
||||
"wildcard_imports"
|
||||
| "enum_glob_use"
|
||||
| "redundant_pub_crate"
|
||||
| "macro_use_imports"
|
||||
| "unsafe_removed_from_name"
|
||||
| "module_name_repetitions"
|
||||
| "single_component_path_imports"
|
||||
)
|
||||
})
|
||||
if let NestedMetaItem::MetaItem(meta_item) = lint
|
||||
&& meta_item.is_word()
|
||||
&& let Some(ident) = meta_item.ident()
|
||||
&& matches!(
|
||||
ident.name.as_str(),
|
||||
"ambiguous_glob_reexports"
|
||||
| "dead_code"
|
||||
| "deprecated"
|
||||
| "hidden_glob_reexports"
|
||||
| "unreachable_pub"
|
||||
| "unused"
|
||||
| "unused_braces"
|
||||
| "unused_import_braces"
|
||||
| "unused_imports"
|
||||
)
|
||||
{
|
||||
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(..) => {
|
||||
if is_word(lint, sym::unused_imports) && skip_unused_imports {
|
||||
|
@ -49,7 +49,7 @@ impl LintConfig {
|
||||
|
||||
type LintTable = BTreeMap<Spanned<String>, Spanned<LintConfig>>;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
struct Lints {
|
||||
#[serde(default)]
|
||||
rust: LintTable,
|
||||
@ -57,9 +57,18 @@ struct Lints {
|
||||
clippy: LintTable,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
struct Workspace {
|
||||
#[serde(default)]
|
||||
lints: Lints,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct CargoToml {
|
||||
#[serde(default)]
|
||||
lints: Lints,
|
||||
#[serde(default)]
|
||||
workspace: Workspace,
|
||||
}
|
||||
|
||||
#[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.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`],
|
||||
/// where the result depends on:
|
||||
///
|
||||
/// - the number of negative values in the entire expression, or
|
||||
/// - the number of negative values on the left hand side of the expression.
|
||||
///
|
||||
/// 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:
|
||||
///
|
||||
/// - all the expressions being positive, or
|
||||
/// - all the expressions being negative.
|
||||
///
|
||||
/// Ignores overflow.
|
||||
///
|
||||
/// 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_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO,
|
||||
crate::disallowed_types::DISALLOWED_TYPES_INFO,
|
||||
crate::doc::DOC_LAZY_CONTINUATION_INFO,
|
||||
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
|
||||
crate::doc::DOC_MARKDOWN_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_UNIT_INFO,
|
||||
crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
|
||||
crate::functions::RENAMED_FUNCTION_PARAMS_INFO,
|
||||
crate::functions::RESULT_LARGE_ERR_INFO,
|
||||
crate::functions::RESULT_UNIT_ERR_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_LET_LOOP_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::main_recursion::MAIN_RECURSION_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::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_doc_hidden, return_ty};
|
||||
@ -6,15 +7,13 @@ use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC};
|
||||
|
||||
pub fn check(
|
||||
cx: &LateContext<'_>,
|
||||
owner_id: OwnerId,
|
||||
sig: FnSig<'_>,
|
||||
headers: DocHeaders,
|
||||
body_id: Option<BodyId>,
|
||||
panic_span: Option<Span>,
|
||||
panic_info: Option<(Span, bool)>,
|
||||
check_private_items: bool,
|
||||
) {
|
||||
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(
|
||||
cx,
|
||||
MISSING_PANICS_DOC,
|
||||
span,
|
||||
"docs for function which may panic missing `# Panics` section",
|
||||
panic_span,
|
||||
panic_info.map(|el| el.0),
|
||||
"first possible panic found here",
|
||||
);
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
mod lazy_continuation;
|
||||
use clippy_utils::attrs::is_doc_hidden;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
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::{
|
||||
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 rustc_ast::ast::Attribute;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
@ -362,6 +363,63 @@ declare_clippy_lint! {
|
||||
"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)]
|
||||
pub struct Documentation {
|
||||
valid_idents: FxHashSet<String>,
|
||||
@ -388,6 +446,7 @@ impl_lint_pass!(Documentation => [
|
||||
UNNECESSARY_SAFETY_DOC,
|
||||
SUSPICIOUS_DOC_COMMENTS,
|
||||
EMPTY_DOCS,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
]);
|
||||
|
||||
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)) {
|
||||
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(
|
||||
cx,
|
||||
item.owner_id,
|
||||
sig,
|
||||
headers,
|
||||
Some(body_id),
|
||||
panic_span,
|
||||
panic_info,
|
||||
self.check_private_items,
|
||||
);
|
||||
}
|
||||
@ -551,6 +610,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
|
||||
cx,
|
||||
valid_idents,
|
||||
parser.into_offset_iter(),
|
||||
&doc,
|
||||
Fragments {
|
||||
fragments: &fragments,
|
||||
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"];
|
||||
|
||||
enum Container {
|
||||
Blockquote,
|
||||
List(usize),
|
||||
}
|
||||
|
||||
/// Checks parsed documentation.
|
||||
/// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`,
|
||||
/// 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<'_>,
|
||||
valid_idents: &FxHashSet<String>,
|
||||
events: Events,
|
||||
doc: &str,
|
||||
fragments: Fragments<'_>,
|
||||
) -> DocHeaders {
|
||||
// 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_link = None;
|
||||
let mut in_heading = false;
|
||||
let mut in_footnote_definition = false;
|
||||
let mut is_rust = false;
|
||||
let mut no_test = 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 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 {
|
||||
Html(tag) => {
|
||||
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;
|
||||
}
|
||||
},
|
||||
Start(BlockQuote) => blockquote_level += 1,
|
||||
End(BlockQuote) => blockquote_level -= 1,
|
||||
Start(BlockQuote) => {
|
||||
blockquote_level += 1;
|
||||
containers.push(Container::Blockquote);
|
||||
},
|
||||
End(BlockQuote) => {
|
||||
blockquote_level -= 1;
|
||||
containers.pop();
|
||||
},
|
||||
Start(CodeBlock(ref kind)) => {
|
||||
in_code = true;
|
||||
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 {
|
||||
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;
|
||||
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 {
|
||||
in_heading = false;
|
||||
}
|
||||
if let End(Item) = event {
|
||||
containers.pop();
|
||||
}
|
||||
if ticks_unbalanced && let Some(span) = fragments.span(cx, paragraph_range.clone()) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
@ -658,8 +745,26 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||
}
|
||||
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
|
||||
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) => {
|
||||
paragraph_range.end = range.end;
|
||||
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> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
is_const: bool,
|
||||
panic_span: Option<Span>,
|
||||
typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
||||
}
|
||||
@ -710,14 +816,15 @@ impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
||||
body: impl Visitable<'tcx>,
|
||||
) -> Option<Span> {
|
||||
) -> Option<(Span, bool)> {
|
||||
let mut vis = Self {
|
||||
cx,
|
||||
is_const: false,
|
||||
panic_span: None,
|
||||
typeck_results,
|
||||
};
|
||||
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"
|
||||
)
|
||||
{
|
||||
self.is_const = in_constant(self.cx, expr.hir_id);
|
||||
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,
|
||||
};
|
||||
|
||||
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 {
|
||||
span_lint_hir(
|
||||
|
@ -1,12 +1,12 @@
|
||||
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::{is_expn_of, path_def_id};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{sym, ExpnId};
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
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 {
|
||||
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"),
|
||||
_ => 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;
|
||||
};
|
||||
|
||||
@ -83,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
||||
};
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
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(
|
||||
cx,
|
||||
EXPLICIT_WRITE,
|
||||
|
@ -1,5 +1,5 @@
|
||||
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::sugg::Sugg;
|
||||
use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
|
||||
@ -7,7 +7,7 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -39,13 +39,24 @@ declare_clippy_lint! {
|
||||
"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 {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx 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)
|
||||
&& 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 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::is_diag_trait_item;
|
||||
use clippy_utils::macros::{
|
||||
find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro,
|
||||
is_format_macro, is_panic, matching_root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall,
|
||||
find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro,
|
||||
is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall,
|
||||
};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{implements_trait, is_type_lang_item};
|
||||
@ -167,15 +167,18 @@ impl_lint_pass!(FormatArgs => [
|
||||
UNUSED_FORMAT_SPECS,
|
||||
]);
|
||||
|
||||
#[allow(clippy::struct_field_names)]
|
||||
pub struct FormatArgs {
|
||||
format_args: FormatArgsStorage,
|
||||
msrv: Msrv,
|
||||
ignore_mixed: bool,
|
||||
}
|
||||
|
||||
impl FormatArgs {
|
||||
#[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 {
|
||||
format_args,
|
||||
msrv,
|
||||
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>) {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||
&& 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 {
|
||||
cx,
|
||||
expr,
|
||||
macro_call: ¯o_call,
|
||||
format_args: &format_args,
|
||||
format_args,
|
||||
ignore_mixed: self.ignore_mixed,
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 rustc_ast::{FormatArgsPiece, FormatTrait};
|
||||
use rustc_errors::Applicability;
|
||||
@ -99,13 +99,15 @@ struct FormatTraitNames {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatImpl {
|
||||
format_args: FormatArgsStorage,
|
||||
// Whether we are inside Display or Debug trait impl - None for neither
|
||||
format_trait_impl: Option<FormatTraitNames>,
|
||||
}
|
||||
|
||||
impl FormatImpl {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(format_args: FormatArgsStorage) -> Self {
|
||||
Self {
|
||||
format_args,
|
||||
format_trait_impl: None,
|
||||
}
|
||||
}
|
||||
@ -129,6 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
|
||||
if let Some(format_trait_impl) = self.format_trait_impl {
|
||||
let linter = FormatImplExpr {
|
||||
cx,
|
||||
format_args: &self.format_args,
|
||||
expr,
|
||||
format_trait_impl,
|
||||
};
|
||||
@ -141,6 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
|
||||
|
||||
struct FormatImplExpr<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
format_args: &'a FormatArgsStorage,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
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)
|
||||
&& let macro_def_id = outer_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 {
|
||||
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
||||
|
@ -181,6 +181,9 @@ fn convert_to_from(
|
||||
let from = snippet_opt(cx, self_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![
|
||||
// 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
|
||||
// ~~~~ ~~~~
|
||||
(self_ident.span, format!("val: {from}")),
|
||||
];
|
||||
|
||||
if let FnRetTy::Return(_) = sig.decl.output {
|
||||
// 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 {
|
||||
cx,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_integer_literal;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
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_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind};
|
||||
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>) {
|
||||
if let ExprKind::Call(maybe_path, [src, radix]) = &exp.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
|
||||
&& let TyKind::Path(ty_qpath) = &ty.kind
|
||||
|
@ -2,15 +2,17 @@ mod impl_trait_in_params;
|
||||
mod misnamed_getters;
|
||||
mod must_use;
|
||||
mod not_unsafe_ptr_arg_deref;
|
||||
mod renamed_function_params;
|
||||
mod result;
|
||||
mod too_many_arguments;
|
||||
mod too_many_lines;
|
||||
|
||||
use clippy_utils::def_path_def_ids;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::def_id::{DefIdSet, LocalDefId};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -359,13 +361,51 @@ declare_clippy_lint! {
|
||||
"`impl Trait` is used in the function's parameters"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[allow(clippy::struct_field_names)]
|
||||
declare_clippy_lint! {
|
||||
/// ### 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 {
|
||||
too_many_arguments_threshold: u64,
|
||||
too_many_lines_threshold: u64,
|
||||
large_error_threshold: u64,
|
||||
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 {
|
||||
@ -374,12 +414,15 @@ impl Functions {
|
||||
too_many_lines_threshold: u64,
|
||||
large_error_threshold: u64,
|
||||
avoid_breaking_exported_api: bool,
|
||||
allow_renamed_params_for: Vec<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
too_many_arguments_threshold,
|
||||
too_many_lines_threshold,
|
||||
large_error_threshold,
|
||||
avoid_breaking_exported_api,
|
||||
allow_renamed_params_for,
|
||||
trait_ids: DefIdSet::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -395,6 +438,7 @@ impl_lint_pass!(Functions => [
|
||||
RESULT_LARGE_ERR,
|
||||
MISNAMED_GETTERS,
|
||||
IMPL_TRAIT_IN_PARAMS,
|
||||
RENAMED_FUNCTION_PARAMS,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Functions {
|
||||
@ -424,6 +468,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
|
||||
must_use::check_impl_item(cx, item);
|
||||
result::check_impl_item(cx, item, self.large_error_threshold);
|
||||
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<'_>) {
|
||||
@ -433,4 +478,12 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
|
||||
result::check_trait_item(cx, item, self.large_error_threshold);
|
||||
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_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind};
|
||||
use rustc_middle::ty::print::PrintTraitRefExt;
|
||||
use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{sym, Span};
|
||||
|
@ -61,11 +61,6 @@ extern crate clippy_utils;
|
||||
#[macro_use]
|
||||
extern crate declare_clippy_lint;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_lint::{Lint, LintId};
|
||||
|
||||
#[cfg(feature = "internal")]
|
||||
pub mod deprecated_lints;
|
||||
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
|
||||
@ -199,6 +194,7 @@ mod lifetimes;
|
||||
mod lines_filter_map_ok;
|
||||
mod literal_representation;
|
||||
mod loops;
|
||||
mod macro_metavars_in_unsafe;
|
||||
mod macro_use;
|
||||
mod main_recursion;
|
||||
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`
|
||||
|
||||
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
|
||||
///
|
||||
@ -597,9 +597,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||
ref allowed_duplicate_crates,
|
||||
allow_comparison_to_zero,
|
||||
ref allowed_prefixes,
|
||||
ref allow_renamed_params_for,
|
||||
|
||||
blacklisted_names: _,
|
||||
cyclomatic_complexity_threshold: _,
|
||||
warn_unsafe_macro_metavars_in_private_macros,
|
||||
} = *conf;
|
||||
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
|
||||
#[cfg(feature = "internal")]
|
||||
{
|
||||
@ -656,7 +666,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||
.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::author::Author));
|
||||
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_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
|
||||
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
|
||||
let format_args = format_args_storage.clone();
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(methods::Methods::new(
|
||||
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_unwrap_in_tests,
|
||||
allowed_dotfiles.clone(),
|
||||
format_args.clone(),
|
||||
))
|
||||
});
|
||||
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(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(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(overflow_check_conditional::OverflowCheckConditional));
|
||||
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,
|
||||
large_error_threshold,
|
||||
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)));
|
||||
@ -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(unused_io_amount::UnusedIoAmount));
|
||||
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(move |tcx| {
|
||||
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_early_pass(|| Box::new(reference::DerefAddrOf));
|
||||
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(else_if_without_else::ElseIfWithoutElse));
|
||||
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,
|
||||
))
|
||||
});
|
||||
store
|
||||
.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined_format_args)));
|
||||
let format_args = format_args_storage.clone();
|
||||
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_early_pass(|| Box::new(octal_escapes::OctalEscapes));
|
||||
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::<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(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 |_| {
|
||||
Box::new(cargo::Cargo {
|
||||
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(manual_unwrap_or_default::ManualUnwrapOrDefault));
|
||||
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`
|
||||
}
|
||||
|
||||
|
@ -60,12 +60,9 @@ fn check_for_mutation(
|
||||
span_low: None,
|
||||
span_high: None,
|
||||
};
|
||||
ExprUseVisitor::for_clippy(
|
||||
cx,
|
||||
body.hir_id.owner.def_id,
|
||||
&mut delegate,
|
||||
)
|
||||
.walk_expr(body).into_ok();
|
||||
ExprUseVisitor::for_clippy(cx, body.hir_id.owner.def_id, &mut delegate)
|
||||
.walk_expr(body)
|
||||
.into_ok();
|
||||
|
||||
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.
|
||||
/// In summary, this pattern consists of two if statements that meet many criteria,
|
||||
///
|
||||
/// - binary operators that are one of [`>`, `<`, `>=`, `<=`].
|
||||
///
|
||||
/// - Both binary statements must have a shared argument
|
||||
///
|
||||
/// - 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 terms of Rust `std` library, the following ranges are acceptable
|
||||
///
|
||||
/// - `Range`
|
||||
/// - `RangeInclusive`
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// - 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.
|
||||
fn is_clamp_meta_pattern<'tcx>(
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 core::cmp::Ordering;
|
||||
use core::{iter, slice};
|
||||
@ -9,9 +9,9 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd};
|
||||
use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{ErrorGuaranteed, Symbol};
|
||||
use rustc_span::{ErrorGuaranteed, Span, Symbol};
|
||||
|
||||
use super::MATCH_SAME_ARMS;
|
||||
|
||||
@ -110,20 +110,22 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
||||
&& check_same_body()
|
||||
};
|
||||
|
||||
let mut appl = Applicability::MaybeIncorrect;
|
||||
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
|
||||
for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
|
||||
if matches!(arm2.pat.kind, PatKind::Wild) {
|
||||
if !cx.tcx.features().non_exhaustive_omitted_patterns_lint
|
||||
|| 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(
|
||||
cx,
|
||||
MATCH_SAME_ARMS,
|
||||
arm1.hir_id,
|
||||
arm1.span,
|
||||
arm_span,
|
||||
"this match arm has an identical body to the `_` wildcard arm",
|
||||
|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")
|
||||
.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,
|
||||
"this match arm has an identical body to another arm",
|
||||
|diag| {
|
||||
let move_pat_snip = snippet(cx, move_arm.pat.span, "<pat2>");
|
||||
let keep_pat_snip = snippet(cx, keep_arm.pat.span, "<pat1>");
|
||||
let move_pat_snip = snippet_with_applicability(cx, move_arm.pat.span, "<pat2>", &mut appl);
|
||||
let keep_pat_snip = snippet_with_applicability(cx, keep_arm.pat.span, "<pat1>", &mut appl);
|
||||
|
||||
diag.span_suggestion(
|
||||
keep_arm.pat.span,
|
||||
"try merging the arm patterns",
|
||||
"or try merging the arm patterns",
|
||||
format!("{keep_pat_snip} | {move_pat_snip}"),
|
||||
Applicability::MaybeIncorrect,
|
||||
appl,
|
||||
)
|
||||
.help("or try changing either arm body")
|
||||
.span_note(move_arm.span, "other arm here");
|
||||
.span_suggestion(
|
||||
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)]
|
||||
enum NormalizedPat<'a> {
|
||||
Wild,
|
||||
|
@ -115,45 +115,60 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
|
||||
self.cx.typeck_results().expr_ty(ex)
|
||||
fn is_sig_drop_expr(&mut self, ex: &'tcx Expr<'_>) -> bool {
|
||||
!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 {
|
||||
!self.seen_types.insert(ty)
|
||||
fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
rustc_middle::ty::Adt(a, b) => {
|
||||
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,
|
||||
if !self.seen_types.insert(ty) {
|
||||
return 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() {
|
||||
return;
|
||||
}
|
||||
let ty = self.sig_drop_checker.get_type(expr);
|
||||
let ty = self.cx.typeck_results().expr_ty(expr);
|
||||
if ty.is_ref() {
|
||||
// We checked that the type was ref, so builtin_deref will return Some,
|
||||
// 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> {
|
||||
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
|
||||
if !self.is_chain_end
|
||||
&& self
|
||||
.sig_drop_checker
|
||||
.has_sig_drop_attr(self.cx, self.sig_drop_checker.get_type(ex))
|
||||
{
|
||||
if !self.is_chain_end && self.sig_drop_checker.is_sig_drop_expr(ex) {
|
||||
self.has_significant_drop = true;
|
||||
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> {
|
||||
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
||||
if self
|
||||
.sig_drop_checker
|
||||
.has_sig_drop_attr(self.sig_drop_checker.cx, self.sig_drop_checker.get_type(ex))
|
||||
{
|
||||
if self.sig_drop_checker.is_sig_drop_expr(ex) {
|
||||
self.found_sig_drop_spans.insert(ex.span);
|
||||
return;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||
use rustc_errors::Applicability;
|
||||
@ -16,6 +16,7 @@ use super::EXPECT_FUN_CALL;
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
format_args_storage: &FormatArgsStorage,
|
||||
expr: &hir::Expr<'_>,
|
||||
method_span: Span,
|
||||
name: &str,
|
||||
@ -134,9 +135,9 @@ pub(super) fn check<'tcx>(
|
||||
// Special handling for `format!` as 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)
|
||||
&& 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);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -126,15 +126,15 @@ enum FilterType {
|
||||
///
|
||||
/// How this is done:
|
||||
/// 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
|
||||
/// `(x as Iterator).filter({filter_arg})` method call.
|
||||
/// 2. we check that we are in a trait method. Therefore we are in an `(x as
|
||||
/// 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
|
||||
/// twice, and we already have a specialized lint for that.
|
||||
/// 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
|
||||
/// Result.
|
||||
/// Result.
|
||||
/// 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`
|
||||
fn expression_type(
|
||||
cx: &LateContext<'_>,
|
||||
|
@ -12,8 +12,10 @@ use rustc_middle::ty;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
/// lint use of:
|
||||
///
|
||||
/// - `hashmap.iter().map(|(_, v)| v)`
|
||||
/// - `hashmap.into_iter().map(|(_, v)| v)`
|
||||
///
|
||||
/// on `HashMaps` and `BTreeMaps` in std
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
|
@ -69,12 +69,9 @@ pub(super) fn check<'tcx>(
|
||||
used_move: HirIdSet::default(),
|
||||
};
|
||||
|
||||
ExprUseVisitor::for_clippy(
|
||||
cx,
|
||||
closure.def_id,
|
||||
&mut delegate,
|
||||
)
|
||||
.consume_body(body).into_ok();
|
||||
ExprUseVisitor::for_clippy(cx, closure.def_id, &mut delegate)
|
||||
.consume_body(body)
|
||||
.into_ok();
|
||||
|
||||
let mut to_be_discarded = false;
|
||||
|
||||
|
@ -133,6 +133,7 @@ use bind_instead_of_map::BindInsteadOfMap;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
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::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty};
|
||||
pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES;
|
||||
@ -4087,12 +4088,14 @@ declare_clippy_lint! {
|
||||
suspicious,
|
||||
"is_empty() called on strings known at compile time"
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Msrv,
|
||||
allow_expect_in_tests: bool,
|
||||
allow_unwrap_in_tests: bool,
|
||||
allowed_dotfiles: FxHashSet<String>,
|
||||
format_args: FormatArgsStorage,
|
||||
}
|
||||
|
||||
impl Methods {
|
||||
@ -4103,6 +4106,7 @@ impl Methods {
|
||||
allow_expect_in_tests: bool,
|
||||
allow_unwrap_in_tests: bool,
|
||||
mut allowed_dotfiles: FxHashSet<String>,
|
||||
format_args: FormatArgsStorage,
|
||||
) -> Self {
|
||||
allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES.iter().map(ToString::to_string));
|
||||
|
||||
@ -4112,6 +4116,7 @@ impl Methods {
|
||||
allow_expect_in_tests,
|
||||
allow_unwrap_in_tests,
|
||||
allowed_dotfiles,
|
||||
format_args,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4281,7 +4286,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
ExprKind::MethodCall(method_call, receiver, args, _) => {
|
||||
let method_span = method_call.ident.span;
|
||||
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_ref_ptr::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::source::snippet_opt;
|
||||
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_hir::def_id::DefId;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{sym, Symbol};
|
||||
|
||||
@ -40,6 +42,53 @@ pub fn check_for_loop_iter(
|
||||
&& !clone_or_copy_needed
|
||||
&& 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
|
||||
&& maybe_iter_method_name.ident.name == sym::iter
|
||||
&& 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_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
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::symbol::Ident;
|
||||
use std::iter;
|
||||
|
@ -120,6 +120,7 @@ fn pat_bindings(pat: &Pat<'_>) -> Vec<HirId> {
|
||||
/// operations performed on `binding_hir_ids` are:
|
||||
/// * to take non-mutable references to them
|
||||
/// * 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
|
||||
/// when `CloneOrCopyVisitor` is done visiting.
|
||||
struct CloneOrCopyVisitor<'cx, 'tcx> {
|
||||
|
@ -96,10 +96,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||
if adj
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
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::ty::{implements_trait, is_copy};
|
||||
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_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::{Rvalue, StatementKind};
|
||||
use rustc_middle::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(
|
||||
cx,
|
||||
&mut self.possible_borrowers,
|
||||
fn_id,
|
||||
cx.typeck_results().node_args(hir_id),
|
||||
i,
|
||||
@ -155,11 +153,9 @@ fn path_has_args(p: &QPath<'_>) -> bool {
|
||||
/// The following constraints will be checked:
|
||||
/// * The borrowed expression meets all the generic type's constraints.
|
||||
/// * 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.
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
/// * The borrowed value is Copy itself OR not a variable (created by a function call)
|
||||
fn needless_borrow_count<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||
fn_id: DefId,
|
||||
callee_args: ty::GenericArgsRef<'tcx>,
|
||||
arg_index: usize,
|
||||
@ -234,9 +230,9 @@ fn needless_borrow_count<'tcx>(
|
||||
|
||||
let referent_ty = cx.typeck_results().expr_ty(referent);
|
||||
|
||||
if !is_copy(cx, referent_ty)
|
||||
&& (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
|
||||
|| !referent_used_exactly_once(cx, possible_borrowers, reference))
|
||||
if (!is_copy(cx, referent_ty) && !referent_ty.is_ref())
|
||||
&& let ExprKind::AddrOf(_, _, inner) = reference.kind
|
||||
&& !matches!(inner.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
|
||||
{
|
||||
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
|
||||
// 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`.
|
||||
@ -408,7 +373,11 @@ fn replace_types<'tcx>(
|
||||
&& let Some(term_ty) = projection_predicate.term.ty()
|
||||
&& 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)
|
||||
&& 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
|
||||
///
|
||||
/// - The expression is a `continue` node.
|
||||
/// - The expression node is a block with the first statement being a
|
||||
/// `continue`.
|
||||
/// - The expression node is a block with the first statement being a `continue`.
|
||||
fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool {
|
||||
match else_expr.kind {
|
||||
ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label),
|
||||
|
@ -273,24 +273,16 @@ fn check<'tcx>(
|
||||
msg_span,
|
||||
"unneeded late initialization",
|
||||
|diag| {
|
||||
diag.tool_only_span_suggestion(
|
||||
local_stmt.span,
|
||||
"remove the local",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
diag.span_suggestion(
|
||||
assign.lhs_span,
|
||||
format!("declare `{binding_name}` here"),
|
||||
let_snippet,
|
||||
diag.multipart_suggestion(
|
||||
format!("move the declaration `{binding_name}` here"),
|
||||
vec![(local_stmt.span, String::new()), (assign.lhs_span, let_snippet)],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
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(
|
||||
cx,
|
||||
@ -298,30 +290,26 @@ fn check<'tcx>(
|
||||
local_stmt.span,
|
||||
"unneeded late initialization",
|
||||
|diag| {
|
||||
diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
|
||||
|
||||
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);
|
||||
suggestions.push((local_stmt.span, String::new()));
|
||||
suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = ")));
|
||||
|
||||
if usage.needs_semi {
|
||||
diag.span_suggestion(
|
||||
usage.stmt.span.shrink_to_hi(),
|
||||
"add a semicolon after the `if` expression",
|
||||
";",
|
||||
applicability,
|
||||
);
|
||||
suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned()));
|
||||
}
|
||||
|
||||
diag.multipart_suggestion(
|
||||
format!(
|
||||
"move the declaration `{binding_name}` here and remove the assignments from the branches"
|
||||
),
|
||||
suggestions,
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
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(
|
||||
cx,
|
||||
@ -329,29 +317,18 @@ fn check<'tcx>(
|
||||
local_stmt.span,
|
||||
"unneeded late initialization",
|
||||
|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,
|
||||
);
|
||||
if usage.needs_semi {
|
||||
suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned()));
|
||||
}
|
||||
|
||||
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,
|
||||
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()
|
||||
.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(),
|
||||
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();
|
||||
|
||||
|
@ -133,7 +133,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
||||
// function body.
|
||||
let MovedVariablesCtxt { moved_vars } = {
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -94,7 +94,6 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
|
||||
|
||||
fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) {
|
||||
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) {
|
||||
span_lint_hir(
|
||||
cx,
|
||||
@ -109,7 +108,6 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
|
||||
|
||||
fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let Some(def_id) = path_to_local(expr) {
|
||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
||||
self.underscore_bindings.swap_remove(&def_id);
|
||||
}
|
||||
}
|
||||
@ -118,7 +116,11 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
|
||||
impl NoEffect {
|
||||
fn check_no_effect(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||
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() {
|
||||
return false;
|
||||
}
|
||||
|
@ -389,6 +389,10 @@ declare_lint_pass!(StrToString => [STR_TO_STRING]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for StrToString {
|
||||
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
|
||||
&& path.ident.name == sym::to_string
|
||||
&& 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 {
|
||||
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
|
||||
&& path.ident.name == sym::to_string
|
||||
&& 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);
|
||||
if from_ty != int_ty {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
|
@ -251,11 +251,7 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
|
||||
local_id: unwrap_info.local_id,
|
||||
};
|
||||
|
||||
let vis = ExprUseVisitor::for_clippy(
|
||||
self.cx,
|
||||
cond.hir_id.owner.def_id,
|
||||
&mut delegate,
|
||||
);
|
||||
let vis = ExprUseVisitor::for_clippy(self.cx, cond.hir_id.owner.def_id, &mut delegate);
|
||||
vis.walk_expr(cond).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 itertools::Itertools;
|
||||
use rustc_ast::{Crate, Expr, ExprKind, FormatArgs};
|
||||
@ -9,13 +9,20 @@ use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{hygiene, Span};
|
||||
use std::iter::once;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Collects [`rustc_ast::FormatArgs`] so that future late passes can call
|
||||
/// [`clippy_utils::macros::find_format_args`]
|
||||
#[derive(Default)]
|
||||
/// Populates [`FormatArgsStorage`] with AST [`FormatArgs`] nodes
|
||||
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 => []);
|
||||
@ -27,16 +34,12 @@ impl EarlyLintPass for FormatArgsCollector {
|
||||
return;
|
||||
}
|
||||
|
||||
self.format_args
|
||||
.insert(expr.span.with_parent(None), Rc::new((**args).clone()));
|
||||
self.format_args.insert(expr.span.with_parent(None), (**args).clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) {
|
||||
AST_FORMAT_ARGS.with(|ast_format_args| {
|
||||
let result = ast_format_args.set(mem::take(&mut self.format_args));
|
||||
debug_assert!(result.is_ok());
|
||||
});
|
||||
self.storage.set(mem::take(&mut self.format_args));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
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::{is_in_cfg_test, is_in_test_function};
|
||||
use rustc_ast::token::LitKind;
|
||||
@ -236,13 +236,15 @@ declare_clippy_lint! {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Write {
|
||||
format_args: FormatArgsStorage,
|
||||
in_debug_impl: bool,
|
||||
allow_print_in_tests: bool,
|
||||
}
|
||||
|
||||
impl Write {
|
||||
pub fn new(allow_print_in_tests: bool) -> Self {
|
||||
pub fn new(format_args: FormatArgsStorage, allow_print_in_tests: bool) -> Self {
|
||||
Self {
|
||||
format_args,
|
||||
allow_print_in_tests,
|
||||
..Default::default()
|
||||
}
|
||||
@ -307,7 +309,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
|
||||
_ => 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!())`
|
||||
if format_args.span.from_expansion() {
|
||||
return;
|
||||
@ -315,15 +317,15 @@ impl<'tcx> LateLintPass<'tcx> for Write {
|
||||
|
||||
match diag_name {
|
||||
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 => {
|
||||
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 {
|
||||
for piece in &format_args.template {
|
||||
|
@ -193,6 +193,21 @@ pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<
|
||||
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
|
||||
///
|
||||
/// # 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`.
|
||||
///
|
||||
/// For the lower bound, this means that:
|
||||
/// - either there is none
|
||||
/// - or it is the smallest value that can be represented by the range's integer type
|
||||
///
|
||||
/// For the upper bound, this means that:
|
||||
/// - either there is none
|
||||
/// - or it is the largest value that can be represented by the range's integer type and is
|
||||
/// inclusive
|
||||
/// - 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())`)
|
||||
///
|
||||
/// 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 {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
|
@ -5,15 +5,13 @@ use crate::visitors::{for_each_expr, Descend};
|
||||
use arrayvec::ArrayVec;
|
||||
use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
|
||||
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_lint::LateContext;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
|
||||
use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
|
||||
use std::cell::OnceCell;
|
||||
use std::ops::ControlFlow;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
|
||||
sym::assert_eq_macro,
|
||||
@ -388,50 +386,44 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) ->
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
/// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be
|
||||
/// able to access the many features of a [`LateContext`].
|
||||
/// Stores AST [`FormatArgs`] nodes for use in late lint passes, as they are in a desugared form in
|
||||
/// the HIR
|
||||
#[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
|
||||
/// assumption that the early pass that populates the map and the later late passes will all be
|
||||
/// running on the same thread.
|
||||
#[doc(hidden)]
|
||||
pub static AST_FORMAT_ARGS: OnceCell<FxHashMap<Span, Rc<FormatArgs>>> = {
|
||||
static CALLED: AtomicBool = AtomicBool::new(false);
|
||||
debug_assert!(
|
||||
!CALLED.swap(true, Ordering::SeqCst),
|
||||
"incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
|
||||
);
|
||||
|
||||
OnceCell::new()
|
||||
};
|
||||
}
|
||||
|
||||
/// 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)
|
||||
/// See also [`find_format_arg_expr`]
|
||||
pub fn get(&self, cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<&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 {
|
||||
ControlFlow::Continue(Descend::Yes)
|
||||
}
|
||||
} else {
|
||||
ControlFlow::Continue(Descend::Yes)
|
||||
ControlFlow::Continue(Descend::No)
|
||||
}
|
||||
} else {
|
||||
ControlFlow::Continue(Descend::No)
|
||||
}
|
||||
})?;
|
||||
})?;
|
||||
|
||||
AST_FORMAT_ARGS.with(|ast_format_args| {
|
||||
ast_format_args
|
||||
.get()?
|
||||
.get(&format_args_expr.span.with_parent(None))
|
||||
.cloned()
|
||||
})
|
||||
debug_assert!(self.0.get().is_some(), "`FormatArgsStorage` not yet populated");
|
||||
|
||||
self.0.get()?.get(&format_args_expr.span.with_parent(None))
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
@ -3,7 +3,7 @@
|
||||
// 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.
|
||||
|
||||
use clippy_config::msrvs::Msrv;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use hir::LangItem;
|
||||
use rustc_attr::StableSince;
|
||||
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 {
|
||||
check_terminator(tcx, body, bb.terminator(), msrv)?;
|
||||
for stmt in &bb.statements {
|
||||
check_statement(tcx, body, def_id, stmt)?;
|
||||
check_statement(tcx, body, def_id, stmt, msrv)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -102,13 +102,14 @@ fn check_rvalue<'tcx>(
|
||||
def_id: DefId,
|
||||
rvalue: &Rvalue<'tcx>,
|
||||
span: Span,
|
||||
msrv: &Msrv,
|
||||
) -> McfResult {
|
||||
match rvalue {
|
||||
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) => {
|
||||
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::Use(operand)
|
||||
| Rvalue::Cast(
|
||||
@ -122,7 +123,7 @@ fn check_rvalue<'tcx>(
|
||||
| CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer),
|
||||
operand,
|
||||
_,
|
||||
) => check_operand(tcx, operand, span, body),
|
||||
) => check_operand(tcx, operand, span, body, msrv),
|
||||
Rvalue::Cast(
|
||||
CastKind::PointerCoercion(
|
||||
PointerCoercion::UnsafeFnPointer
|
||||
@ -133,15 +134,13 @@ fn check_rvalue<'tcx>(
|
||||
_,
|
||||
) => Err((span, "function pointer casts are not allowed in const fn".into())),
|
||||
Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), op, cast_ty) => {
|
||||
let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
|
||||
deref_ty
|
||||
} else {
|
||||
let Some(pointee_ty) = cast_ty.builtin_deref(true) else {
|
||||
// We cannot allow this for now.
|
||||
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));
|
||||
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.
|
||||
Ok(())
|
||||
} else {
|
||||
@ -162,8 +161,8 @@ fn check_rvalue<'tcx>(
|
||||
)),
|
||||
// binops are fine on integers
|
||||
Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
|
||||
check_operand(tcx, lhs, span, body)?;
|
||||
check_operand(tcx, rhs, span, body)?;
|
||||
check_operand(tcx, lhs, span, body, msrv)?;
|
||||
check_operand(tcx, rhs, span, body, msrv)?;
|
||||
let ty = lhs.ty(body, tcx);
|
||||
if ty.is_integral() || ty.is_bool() || ty.is_char() {
|
||||
Ok(())
|
||||
@ -179,14 +178,14 @@ fn check_rvalue<'tcx>(
|
||||
Rvalue::UnaryOp(_, operand) => {
|
||||
let ty = operand.ty(body, tcx);
|
||||
if ty.is_integral() || ty.is_bool() {
|
||||
check_operand(tcx, operand, span, body)
|
||||
check_operand(tcx, operand, span, body, msrv)
|
||||
} else {
|
||||
Err((span, "only int and `bool` operations are stable in const fn".into()))
|
||||
}
|
||||
},
|
||||
Rvalue::Aggregate(_, operands) => {
|
||||
for operand in operands {
|
||||
check_operand(tcx, operand, span, body)?;
|
||||
check_operand(tcx, operand, span, body, msrv)?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
@ -198,28 +197,29 @@ fn check_statement<'tcx>(
|
||||
body: &Body<'tcx>,
|
||||
def_id: DefId,
|
||||
statement: &Statement<'tcx>,
|
||||
msrv: &Msrv,
|
||||
) -> McfResult {
|
||||
let span = statement.source_info.span;
|
||||
match &statement.kind {
|
||||
StatementKind::Assign(box (place, rval)) => {
|
||||
check_place(tcx, *place, span, body)?;
|
||||
check_rvalue(tcx, body, def_id, rval, span)
|
||||
check_place(tcx, *place, span, body, msrv)?;
|
||||
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
|
||||
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(
|
||||
rustc_middle::mir::CopyNonOverlapping { dst, src, count },
|
||||
)) => {
|
||||
check_operand(tcx, dst, span, body)?;
|
||||
check_operand(tcx, src, span, body)?;
|
||||
check_operand(tcx, count, span, body)
|
||||
check_operand(tcx, dst, span, body, msrv)?;
|
||||
check_operand(tcx, src, span, body, msrv)?;
|
||||
check_operand(tcx, count, span, body, msrv)
|
||||
},
|
||||
// These are all NOPs
|
||||
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 {
|
||||
Operand::Move(place) => {
|
||||
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) {
|
||||
Some(_) => Err((span, "cannot access `static` items in const fn".into())),
|
||||
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() {
|
||||
match elem {
|
||||
ProjectionElem::Field(..) => {
|
||||
let base_ty = base.ty(body, tcx).ty;
|
||||
if let Some(def) = base_ty.ty_adt_def() {
|
||||
// No union field accesses in `const fn`
|
||||
if def.is_union() {
|
||||
return Err((span, "accessing union fields is unstable".into()));
|
||||
}
|
||||
if base.ty(body, tcx).ty.is_union() && !msrv.meets(msrvs::CONST_FN_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::OpaqueCast(..)
|
||||
| ProjectionElem::Downcast(..)
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::Deref
|
||||
| ProjectionElem::Subtype(_)
|
||||
| ProjectionElem::Index(_) => {},
|
||||
}
|
||||
@ -304,7 +314,7 @@ fn check_terminator<'tcx>(
|
||||
}
|
||||
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 { .. } => {
|
||||
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 {
|
||||
check_operand(tcx, &arg.node, span, body)?;
|
||||
check_operand(tcx, &arg.node, span, body, msrv)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
@ -357,7 +367,7 @@ fn check_terminator<'tcx>(
|
||||
msg: _,
|
||||
target: _,
|
||||
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())),
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_session::Session;
|
||||
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::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.
|
||||
/// - 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
|
||||
/// `HasPlaceholders`
|
||||
/// `HasPlaceholders`
|
||||
pub fn snippet_with_applicability<'a, T: LintContext>(
|
||||
cx: &T,
|
||||
span: Span,
|
||||
|
@ -67,8 +67,7 @@ impl<'a> Sugg<'a> {
|
||||
/// - Applicability level `Unspecified` will never be changed.
|
||||
/// - 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
|
||||
/// `HasPlaceholders`
|
||||
/// to `HasPlaceholders`
|
||||
pub fn hir_with_applicability(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
|
@ -18,8 +18,8 @@ use rustc_middle::traits::EvaluationResult;
|
||||
use rustc_middle::ty::layout::ValidityRequirement;
|
||||
use rustc_middle::ty::{
|
||||
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
|
||||
GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, ToPredicate, TraitRef, Ty, TyCtxt,
|
||||
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
|
||||
GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, ToPredicate, TraitRef, Ty, TyCtxt, TypeSuperVisitable,
|
||||
TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
|
||||
};
|
||||
use rustc_span::symbol::Ident;
|
||||
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 args = args
|
||||
.into_iter()
|
||||
.map(|arg| {
|
||||
arg.into().unwrap_or_else(|| {
|
||||
infcx.next_ty_var(DUMMY_SP).into()
|
||||
})
|
||||
})
|
||||
.map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// If an effect arg was not specified, we need to specify it.
|
||||
@ -795,7 +791,8 @@ fn sig_from_bounds<'tcx>(
|
||||
inputs = Some(i);
|
||||
},
|
||||
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() {
|
||||
// Multiple different fn trait impls. Is this even allowed?
|
||||
@ -956,11 +953,7 @@ pub struct AdtVariantInfo {
|
||||
|
||||
impl AdtVariantInfo {
|
||||
/// Returns ADT variants ordered by size
|
||||
pub fn new<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
adt: AdtDef<'tcx>,
|
||||
subst: GenericArgsRef<'tcx>
|
||||
) -> Vec<Self> {
|
||||
pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: GenericArgsRef<'tcx>) -> Vec<Self> {
|
||||
let mut variants_size = adt
|
||||
.variants()
|
||||
.iter()
|
||||
|
@ -16,13 +16,9 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) ->
|
||||
used_mutably: HirIdSet::default(),
|
||||
skip: false,
|
||||
};
|
||||
ExprUseVisitor::for_clippy(
|
||||
cx,
|
||||
expr.hir_id.owner.def_id,
|
||||
&mut delegate,
|
||||
)
|
||||
.walk_expr(expr)
|
||||
.into_ok();
|
||||
ExprUseVisitor::for_clippy(cx, expr.hir_id.owner.def_id, &mut delegate)
|
||||
.walk_expr(expr)
|
||||
.into_ok();
|
||||
|
||||
if delegate.skip {
|
||||
return None;
|
||||
|
@ -13,7 +13,7 @@ default-run = "lintcheck"
|
||||
[dependencies]
|
||||
anyhow = "1.0.69"
|
||||
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"
|
||||
crossbeam-channel = "0.5.6"
|
||||
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 }
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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]
|
||||
pedantic = { level = "warn", 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
|
||||
--> 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`
|
||||
|
||||
|
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 _: 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-print-in-tests
|
||||
allow-private-module-inception
|
||||
allow-renamed-params-for
|
||||
allow-unwrap-in-tests
|
||||
allow-useless-vec-in-tests
|
||||
allowed-dotfiles
|
||||
@ -74,6 +75,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
|
||||
vec-box-size-threshold
|
||||
verbose-bit-mask-threshold
|
||||
warn-on-all-wildcard-imports
|
||||
warn-unsafe-macro-metavars-in-private-macros
|
||||
--> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:2:1
|
||||
|
|
||||
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-print-in-tests
|
||||
allow-private-module-inception
|
||||
allow-renamed-params-for
|
||||
allow-unwrap-in-tests
|
||||
allow-useless-vec-in-tests
|
||||
allowed-dotfiles
|
||||
@ -155,6 +158,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
|
||||
vec-box-size-threshold
|
||||
verbose-bit-mask-threshold
|
||||
warn-on-all-wildcard-imports
|
||||
warn-unsafe-macro-metavars-in-private-macros
|
||||
--> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:4:1
|
||||
|
|
||||
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-print-in-tests
|
||||
allow-private-module-inception
|
||||
allow-renamed-params-for
|
||||
allow-unwrap-in-tests
|
||||
allow-useless-vec-in-tests
|
||||
allowed-dotfiles
|
||||
@ -236,6 +241,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
|
||||
vec-box-size-threshold
|
||||
verbose-bit-mask-threshold
|
||||
warn-on-all-wildcard-imports
|
||||
warn-unsafe-macro-metavars-in-private-macros
|
||||
--> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:7:1
|
||||
|
|
||||
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);
|
||||
}
|
||||
|
||||
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 {
|
||||
let mut a = HasCloneFrom;
|
||||
for _ in 1..10 {
|
||||
@ -86,6 +96,12 @@ fn assign_to_uninit_mut_var(b: HasCloneFrom) {
|
||||
a = b.clone();
|
||||
}
|
||||
|
||||
fn late_init_let_tuple() {
|
||||
let (p, q): (String, String);
|
||||
p = "ghi".to_string();
|
||||
q = p.clone();
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
impl FakeToOwned {
|
||||
/// 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();
|
||||
}
|
||||
|
||||
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 {
|
||||
let mut a = HasCloneFrom;
|
||||
for _ in 1..10 {
|
||||
@ -86,6 +96,12 @@ fn assign_to_uninit_mut_var(b: HasCloneFrom) {
|
||||
a = b.clone();
|
||||
}
|
||||
|
||||
fn late_init_let_tuple() {
|
||||
let (p, q): (String, String);
|
||||
p = "ghi".to_string();
|
||||
q = p.clone();
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
impl FakeToOwned {
|
||||
/// 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)`
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)`
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)`
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)`
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))`
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))`
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)`
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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)]
|
||||
#![warn(clippy::expl_impl_clone_on_copy)]
|
||||
|
||||
|
||||
#[derive(Copy)]
|
||||
struct Qux;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 | |
|
||||
@ -10,7 +10,7 @@ LL | | }
|
||||
| |_^
|
||||
|
|
||||
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 | |
|
||||
@ -23,7 +23,7 @@ LL | | }
|
||||
= help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]`
|
||||
|
||||
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 | |
|
||||
@ -34,7 +34,7 @@ LL | | }
|
||||
| |_^
|
||||
|
|
||||
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 | |
|
||||
@ -45,7 +45,7 @@ LL | | }
|
||||
| |_^
|
||||
|
||||
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 | |
|
||||
@ -56,7 +56,7 @@ LL | | }
|
||||
| |_^
|
||||
|
|
||||
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 | |
|
||||
@ -67,7 +67,7 @@ LL | | }
|
||||
| |_^
|
||||
|
||||
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 | |
|
||||
@ -78,7 +78,7 @@ LL | | }
|
||||
| |_^
|
||||
|
|
||||
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 | |
|
||||
@ -89,7 +89,7 @@ LL | | }
|
||||
| |_^
|
||||
|
||||
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 | |
|
||||
@ -100,7 +100,7 @@ LL | | }
|
||||
| |_^
|
||||
|
|
||||
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 | |
|
||||
|
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
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![warn(clippy::duplicated_attributes)]
|
||||
#![cfg(any(unix, windows))]
|
||||
#![allow(dead_code)]
|
||||
@ -20,6 +20,10 @@ fn foo() {}
|
||||
#[cfg(unix)] // cfgs are not handled
|
||||
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!
|
||||
fn babar() {}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![feature(const_int_from_str)]
|
||||
#![warn(clippy::from_str_radix_10)]
|
||||
|
||||
mod some_mod {
|
||||
@ -59,3 +60,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
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)]
|
||||
|
||||
mod some_mod {
|
||||
@ -59,3 +60,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
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`
|
||||
--> 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)?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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)]`
|
||||
|
||||
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)?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::<i64>()`
|
||||
|
||||
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)?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::<isize>()`
|
||||
|
||||
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)?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::<u8>()`
|
||||
|
||||
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)?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::<u16>()`
|
||||
|
||||
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)?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::<i128>()`
|
||||
|
||||
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)?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::<i32>()`
|
||||
|
||||
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)?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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
|
||||
|
|
||||
LL | Abc::A => 0,
|
||||
| ^^^^^^^^^^^ help: try removing the arm
|
||||
| ^^^^^^^^^^^^^ help: try removing the arm
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
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
|
||||
|
|
||||
LL | (1, .., 3) => 42,
|
||||
| ----------^^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `(1, .., 3) | (.., 3)`
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> tests/ui/match_same_arms.rs:19:9
|
||||
= help: try changing either arm body
|
||||
help: or try merging the arm patterns
|
||||
|
|
||||
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
|
||||
--> tests/ui/match_same_arms.rs:25:9
|
||||
|
|
||||
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
|
||||
--> tests/ui/match_same_arms.rs:26:9
|
||||
|
|
||||
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
|
||||
--> tests/ui/match_same_arms.rs:33:9
|
||||
|
|
||||
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
|
||||
--> tests/ui/match_same_arms.rs:35:9
|
||||
|
|
||||
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
|
||||
--> tests/ui/match_same_arms.rs:33:9
|
||||
|
|
||||
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
|
||||
--> tests/ui/match_same_arms.rs:52:17
|
||||
|
|
||||
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
|
||||
note: other arm here
|
||||
--> tests/ui/match_same_arms.rs:51:17
|
||||
= help: try changing either arm body
|
||||
help: or try merging the arm patterns
|
||||
|
|
||||
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
|
||||
|
||||
|
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(
|
||||
clippy::disallowed_names,
|
||||
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 foo() -> bool {
|
||||
unimplemented!()
|
||||
|
@ -1,18 +1,18 @@
|
||||
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 | | foo();
|
||||
LL | | let mut a = 42 + [23].len() as i32;
|
||||
LL | | if true {
|
||||
... |
|
||||
LL | | a
|
||||
LL | | },
|
||||
| |_________^ help: try removing the arm
|
||||
LL | | _ => {
|
||||
| |________^ help: try removing the arm
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: `_` wildcard arm here
|
||||
--> tests/ui/match_same_arms2.rs:24:9
|
||||
--> tests/ui/match_same_arms2.rs:25:9
|
||||
|
|
||||
LL | / _ => {
|
||||
LL | | foo();
|
||||
@ -26,203 +26,200 @@ LL | | },
|
||||
= help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
|
||||
|
||||
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(),
|
||||
| --^^^^^^^^^
|
||||
| |
|
||||
| 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
|
||||
--> tests/ui/match_same_arms2.rs:44:9
|
||||
--> tests/ui/match_same_arms2.rs:45:9
|
||||
|
|
||||
LL | None => 24,
|
||||
| ----^^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `None | Some(_)`
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> tests/ui/match_same_arms2.rs:43:9
|
||||
= help: try changing either arm body
|
||||
help: or try merging the arm patterns
|
||||
|
|
||||
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
|
||||
--> tests/ui/match_same_arms2.rs:66:9
|
||||
--> tests/ui/match_same_arms2.rs:67:9
|
||||
|
|
||||
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
|
||||
--> tests/ui/match_same_arms2.rs:80:9
|
||||
--> tests/ui/match_same_arms2.rs:81:9
|
||||
|
|
||||
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
|
||||
--> 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
|
||||
|
|
||||
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
|
||||
--> 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
|
||||
|
|
||||
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
|
||||
--> tests/ui/match_same_arms2.rs:135:9
|
||||
--> tests/ui/match_same_arms2.rs:136:9
|
||||
|
|
||||
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
|
||||
--> tests/ui/match_same_arms2.rs:162:9
|
||||
--> tests/ui/match_same_arms2.rs:163:9
|
||||
|
|
||||
LL | 1 => {
|
||||
| ^ help: try merging the arm patterns: `1 | 0`
|
||||
| _________|
|
||||
| |
|
||||
LL | / 1 => {
|
||||
LL | | empty!(0);
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> tests/ui/match_same_arms2.rs:159:9
|
||||
= help: try changing either arm body
|
||||
help: or try merging the arm patterns
|
||||
|
|
||||
LL | / 0 => {
|
||||
LL | | empty!(0);
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
||||
error: match expression looks like `matches!` macro
|
||||
--> tests/ui/match_same_arms2.rs:181:16
|
||||
LL | 1 | 0 => {
|
||||
| ~~~~~
|
||||
help: and remove this obsolete arm
|
||||
|
|
||||
LL | let _ans = match x {
|
||||
| ________________^
|
||||
LL | | E::A => false,
|
||||
LL | | E::B => false,
|
||||
LL | | _ => true,
|
||||
LL | | };
|
||||
| |_____^ help: try: `!matches!(x, E::A | E::B)`
|
||||
LL - 0 => {
|
||||
LL - empty!(0);
|
||||
LL - },
|
||||
|
|
||||
= 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
|
||||
--> tests/ui/match_same_arms2.rs:213: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
|
||||
--> tests/ui/match_same_arms2.rs:214:9
|
||||
|
|
||||
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
|
||||
--> 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,
|
||||
| ----------------------------^^^^^
|
||||
| |
|
||||
| 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
|
||||
--> tests/ui/match_same_arms2.rs:260:9
|
||||
--> tests/ui/match_same_arms2.rs:261:9
|
||||
|
|
||||
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)]
|
||||
#![warn(clippy::match_same_arms)]
|
||||
#![no_main]
|
||||
//@no-rustfix
|
||||
use std::sync::atomic::Ordering; // #[non_exhaustive] enum
|
||||
|
||||
fn repeat() -> ! {
|
||||
|
@ -1,12 +1,13 @@
|
||||
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(),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm
|
||||
LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(),
|
||||
LL | |
|
||||
| |________^ help: try removing the arm
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
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(),
|
||||
| ^^^^^^^^^^^^^
|
||||
@ -14,14 +15,15 @@ LL | _ => repeat(),
|
||||
= help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
|
||||
|
||||
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(),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm
|
||||
LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(),
|
||||
LL | |
|
||||
| |____________^ help: try removing the arm
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
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(),
|
||||
| ^^^^^^^^^^^^^
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user