mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 08:44:35 +00:00
Merge commit '3c7e7dbc1583a0b06df5bd7623dd354a4debd23d' into clippyup
This commit is contained in:
commit
7a782537b1
@ -3437,9 +3437,11 @@ Released 2018-09-13
|
|||||||
[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
|
[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
|
||||||
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
|
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
|
||||||
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
|
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
|
||||||
|
[`arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic
|
||||||
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
|
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
|
||||||
[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
|
[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
|
||||||
[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
|
[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
|
||||||
|
[`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states
|
||||||
[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
|
[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
|
||||||
[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
|
[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
|
||||||
[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
|
[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
|
||||||
@ -3793,6 +3795,7 @@ Released 2018-09-13
|
|||||||
[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
|
[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
|
||||||
[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
|
[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
|
||||||
[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
|
[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
|
||||||
|
[`obfuscated_if_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#obfuscated_if_else
|
||||||
[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
|
[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
|
||||||
[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
|
[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
|
||||||
[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion
|
[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion
|
||||||
|
@ -32,6 +32,7 @@ compiletest_rs = { version = "0.8", features = ["tmp"] }
|
|||||||
tester = "0.9"
|
tester = "0.9"
|
||||||
regex = "1.5"
|
regex = "1.5"
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
|
walkdir = "2.3"
|
||||||
# This is used by the `collect-metadata` alias.
|
# This is used by the `collect-metadata` alias.
|
||||||
filetime = "0.2"
|
filetime = "0.2"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# GitHub Actions
|
# GitHub Actions
|
||||||
|
|
||||||
On the GitHub hosted runners, Clippy from the latest stable Rust version comes
|
GitHub hosted runners using the latest stable version of Rust have Clippy pre-installed.
|
||||||
pre-installed. So all you have to do is to run `cargo clippy`.
|
It is as simple as running `cargo clippy` to run lints against the codebase.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
on: push
|
on: push
|
||||||
@ -15,7 +15,7 @@ jobs:
|
|||||||
clippy_check:
|
clippy_check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3
|
||||||
- name: Run Clippy
|
- name: Run Clippy
|
||||||
run: cargo clippy --all-targets --all-features
|
run: cargo clippy --all-targets --all-features
|
||||||
```
|
```
|
||||||
|
@ -10,6 +10,10 @@ because that's clearly a non-descriptive name.
|
|||||||
- [Adding a new lint](#adding-a-new-lint)
|
- [Adding a new lint](#adding-a-new-lint)
|
||||||
- [Setup](#setup)
|
- [Setup](#setup)
|
||||||
- [Getting Started](#getting-started)
|
- [Getting Started](#getting-started)
|
||||||
|
- [Defining Our Lint](#defining-our-lint)
|
||||||
|
- [Standalone](#standalone)
|
||||||
|
- [Specific Type](#specific-type)
|
||||||
|
- [Tests Location](#tests-location)
|
||||||
- [Testing](#testing)
|
- [Testing](#testing)
|
||||||
- [Cargo lints](#cargo-lints)
|
- [Cargo lints](#cargo-lints)
|
||||||
- [Rustfix tests](#rustfix-tests)
|
- [Rustfix tests](#rustfix-tests)
|
||||||
@ -36,17 +40,38 @@ See the [Basics](basics.md#get-the-code) documentation.
|
|||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
There is a bit of boilerplate code that needs to be set up when creating a new
|
There is a bit of boilerplate code that needs to be set up when creating a new
|
||||||
lint. Fortunately, you can use the clippy dev tools to handle this for you. We
|
lint. Fortunately, you can use the Clippy dev tools to handle this for you. We
|
||||||
are naming our new lint `foo_functions` (lints are generally written in snake
|
are naming our new lint `foo_functions` (lints are generally written in snake
|
||||||
case), and we don't need type information so it will have an early pass type
|
case), and we don't need type information, so it will have an early pass type
|
||||||
(more on this later on). If you're not sure if the name you chose fits the lint,
|
(more on this later). If you're unsure if the name you chose fits the lint,
|
||||||
take a look at our [lint naming guidelines][lint_naming]. To get started on this
|
take a look at our [lint naming guidelines][lint_naming].
|
||||||
lint you can run `cargo dev new_lint --name=foo_functions --pass=early
|
|
||||||
--category=pedantic` (category will default to nursery if not provided). This
|
## Defining Our Lint
|
||||||
command will create two files: `tests/ui/foo_functions.rs` and
|
To get started, there are two ways to define our lint.
|
||||||
`clippy_lints/src/foo_functions.rs`, as well as [registering the
|
|
||||||
lint](#lint-registration). For cargo lints, two project hierarchies (fail/pass)
|
### Standalone
|
||||||
will be created by default under `tests/ui-cargo`.
|
Command: `cargo dev new_lint --name=foo_functions --pass=early --category=pedantic`
|
||||||
|
(category will default to nursery if not provided)
|
||||||
|
|
||||||
|
This command will create a new file: `clippy_lints/src/foo_functions.rs`, as well
|
||||||
|
as [register the lint](#lint-registration).
|
||||||
|
|
||||||
|
### Specific Type
|
||||||
|
Command: `cargo dev new_lint --name=foo_functions --type=functions --category=pedantic`
|
||||||
|
|
||||||
|
This command will create a new file: `clippy_lints/src/{type}/foo_functions.rs`.
|
||||||
|
|
||||||
|
Notice how this command has a `--type` flag instead of `--pass`. Unlike a standalone
|
||||||
|
definition, this lint won't be registered in the traditional sense. Instead, you will
|
||||||
|
call your lint from within the type's lint pass, found in `clippy_lints/src/{type}/mod.rs`.
|
||||||
|
|
||||||
|
A "type" is just the name of a directory in `clippy_lints/src`, like `functions` in
|
||||||
|
the example command. These are groupings of lints with common behaviors, so if your
|
||||||
|
lint falls into one, it would be best to add it to that type.
|
||||||
|
|
||||||
|
### Tests Location
|
||||||
|
Both commands will create a file: `tests/ui/foo_functions.rs`. For cargo lints,
|
||||||
|
two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`.
|
||||||
|
|
||||||
Next, we'll open up these files and add our lint!
|
Next, we'll open up these files and add our lint!
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ instructions for other options.
|
|||||||
## Make changes
|
## Make changes
|
||||||
|
|
||||||
The book's
|
The book's
|
||||||
[src](https://github.com/joshrotenberg/rust-clippy/tree/clippy_guide/book/src)
|
[src](https://github.com/rust-lang/rust-clippy/tree/master/book/src)
|
||||||
directory contains all of the markdown files used to generate the book. If you
|
directory contains all of the markdown files used to generate the book. If you
|
||||||
want to see your changes in real time, you can use the mdbook `serve` command to
|
want to see your changes in real time, you can use the mdbook `serve` command to
|
||||||
run a web server locally that will automatically update changes as they are
|
run a web server locally that will automatically update changes as they are
|
||||||
|
@ -13,7 +13,7 @@ pub enum CliError {
|
|||||||
IoError(io::Error),
|
IoError(io::Error),
|
||||||
RustfmtNotInstalled,
|
RustfmtNotInstalled,
|
||||||
WalkDirError(walkdir::Error),
|
WalkDirError(walkdir::Error),
|
||||||
RaSetupActive,
|
IntellijSetupActive,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for CliError {
|
impl From<io::Error> for CliError {
|
||||||
@ -48,7 +48,7 @@ pub fn run(check: bool, verbose: bool) {
|
|||||||
.expect("Failed to read clippy Cargo.toml")
|
.expect("Failed to read clippy Cargo.toml")
|
||||||
.contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
|
.contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
|
||||||
{
|
{
|
||||||
return Err(CliError::RaSetupActive);
|
return Err(CliError::IntellijSetupActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
rustfmt_test(context)?;
|
rustfmt_test(context)?;
|
||||||
@ -93,11 +93,11 @@ pub fn run(check: bool, verbose: bool) {
|
|||||||
CliError::WalkDirError(err) => {
|
CliError::WalkDirError(err) => {
|
||||||
eprintln!("error: {}", err);
|
eprintln!("error: {}", err);
|
||||||
},
|
},
|
||||||
CliError::RaSetupActive => {
|
CliError::IntellijSetupActive => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.
|
"error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.
|
||||||
Not formatting because that would format the local repo as well!
|
Not formatting because that would format the local repo as well!
|
||||||
Please revert the changes to Cargo.tomls first."
|
Please revert the changes to Cargo.tomls with `cargo dev remove intellij`."
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,8 @@ fn main() {
|
|||||||
match new_lint::create(
|
match new_lint::create(
|
||||||
matches.get_one::<String>("pass"),
|
matches.get_one::<String>("pass"),
|
||||||
matches.get_one::<String>("name"),
|
matches.get_one::<String>("name"),
|
||||||
matches.get_one::<String>("category"),
|
matches.get_one::<String>("category").map(String::as_str),
|
||||||
|
matches.get_one::<String>("type").map(String::as_str),
|
||||||
matches.contains_id("msrv"),
|
matches.contains_id("msrv"),
|
||||||
) {
|
) {
|
||||||
Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
|
Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
|
||||||
@ -157,7 +158,8 @@ fn get_clap_config() -> ArgMatches {
|
|||||||
.help("Specify whether the lint runs during the early or late pass")
|
.help("Specify whether the lint runs during the early or late pass")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_parser([PossibleValue::new("early"), PossibleValue::new("late")])
|
.value_parser([PossibleValue::new("early"), PossibleValue::new("late")])
|
||||||
.required(true),
|
.conflicts_with("type")
|
||||||
|
.required_unless_present("type"),
|
||||||
Arg::new("name")
|
Arg::new("name")
|
||||||
.short('n')
|
.short('n')
|
||||||
.long("name")
|
.long("name")
|
||||||
@ -183,6 +185,11 @@ fn get_clap_config() -> ArgMatches {
|
|||||||
PossibleValue::new("internal_warn"),
|
PossibleValue::new("internal_warn"),
|
||||||
])
|
])
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
|
Arg::new("type")
|
||||||
|
.long("type")
|
||||||
|
.help("What directory the lint belongs in")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(false),
|
||||||
Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"),
|
Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"),
|
||||||
]),
|
]),
|
||||||
Command::new("setup")
|
Command::new("setup")
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::clippy_project_root;
|
use crate::clippy_project_root;
|
||||||
use indoc::indoc;
|
use indoc::{indoc, writedoc};
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
use std::fs::{self, OpenOptions};
|
use std::fs::{self, OpenOptions};
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
@ -10,6 +10,7 @@ struct LintData<'a> {
|
|||||||
pass: &'a str,
|
pass: &'a str,
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
category: &'a str,
|
category: &'a str,
|
||||||
|
ty: Option<&'a str>,
|
||||||
project_root: PathBuf,
|
project_root: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,26 +38,44 @@ impl<T> Context for io::Result<T> {
|
|||||||
pub fn create(
|
pub fn create(
|
||||||
pass: Option<&String>,
|
pass: Option<&String>,
|
||||||
lint_name: Option<&String>,
|
lint_name: Option<&String>,
|
||||||
category: Option<&String>,
|
category: Option<&str>,
|
||||||
|
mut ty: Option<&str>,
|
||||||
msrv: bool,
|
msrv: bool,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
|
if category == Some("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 {
|
let lint = LintData {
|
||||||
pass: pass.expect("`pass` argument is validated by clap"),
|
pass: pass.map_or("", String::as_str),
|
||||||
name: lint_name.expect("`name` argument is validated by clap"),
|
name: lint_name.expect("`name` argument is validated by clap"),
|
||||||
category: category.expect("`category` argument is validated by clap"),
|
category: category.expect("`category` argument is validated by clap"),
|
||||||
|
ty,
|
||||||
project_root: clippy_project_root(),
|
project_root: clippy_project_root(),
|
||||||
};
|
};
|
||||||
|
|
||||||
create_lint(&lint, msrv).context("Unable to create lint implementation")?;
|
create_lint(&lint, msrv).context("Unable to create lint implementation")?;
|
||||||
create_test(&lint).context("Unable to create a test for the new lint")?;
|
create_test(&lint).context("Unable to create a test for the new lint")?;
|
||||||
add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")
|
|
||||||
|
if lint.ty.is_none() {
|
||||||
|
add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
|
fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
|
||||||
let lint_contents = get_lint_file_contents(lint, enable_msrv);
|
if let Some(ty) = lint.ty {
|
||||||
|
create_lint_for_ty(lint, enable_msrv, ty)
|
||||||
|
} else {
|
||||||
|
let lint_contents = get_lint_file_contents(lint, enable_msrv);
|
||||||
|
let lint_path = format!("clippy_lints/src/{}.rs", lint.name);
|
||||||
|
write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?;
|
||||||
|
println!("Generated lint file: `{}`", lint_path);
|
||||||
|
|
||||||
let lint_path = format!("clippy_lints/src/{}.rs", lint.name);
|
Ok(())
|
||||||
write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_test(lint: &LintData<'_>) -> io::Result<()> {
|
fn create_test(lint: &LintData<'_>) -> io::Result<()> {
|
||||||
@ -75,16 +94,22 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> {
|
|||||||
|
|
||||||
if lint.category == "cargo" {
|
if lint.category == "cargo" {
|
||||||
let relative_test_dir = format!("tests/ui-cargo/{}", lint.name);
|
let relative_test_dir = format!("tests/ui-cargo/{}", lint.name);
|
||||||
let test_dir = lint.project_root.join(relative_test_dir);
|
let test_dir = lint.project_root.join(&relative_test_dir);
|
||||||
fs::create_dir(&test_dir)?;
|
fs::create_dir(&test_dir)?;
|
||||||
|
|
||||||
create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?;
|
create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?;
|
||||||
create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")
|
create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")?;
|
||||||
|
|
||||||
|
println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`");
|
||||||
} else {
|
} else {
|
||||||
let test_path = format!("tests/ui/{}.rs", lint.name);
|
let test_path = format!("tests/ui/{}.rs", lint.name);
|
||||||
let test_contents = get_test_file_contents(lint.name, None);
|
let test_contents = get_test_file_contents(lint.name, None);
|
||||||
write_file(lint.project_root.join(test_path), test_contents)
|
write_file(lint.project_root.join(&test_path), test_contents)?;
|
||||||
|
|
||||||
|
println!("Generated test file: `{}`", test_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
|
fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
|
||||||
@ -204,7 +229,6 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let version = get_stabilization_version();
|
|
||||||
let lint_name = lint.name;
|
let lint_name = lint.name;
|
||||||
let category = lint.category;
|
let category = lint.category;
|
||||||
let name_camel = to_camel_case(lint.name);
|
let name_camel = to_camel_case(lint.name);
|
||||||
@ -238,32 +262,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let _ = write!(
|
let _ = write!(result, "{}", get_lint_declaration(&name_upper, category));
|
||||||
result,
|
|
||||||
indoc! {r#"
|
|
||||||
declare_clippy_lint! {{
|
|
||||||
/// ### What it does
|
|
||||||
///
|
|
||||||
/// ### Why is this bad?
|
|
||||||
///
|
|
||||||
/// ### Example
|
|
||||||
/// ```rust
|
|
||||||
/// // example code where clippy issues a warning
|
|
||||||
/// ```
|
|
||||||
/// Use instead:
|
|
||||||
/// ```rust
|
|
||||||
/// // example code which does not raise clippy warning
|
|
||||||
/// ```
|
|
||||||
#[clippy::version = "{version}"]
|
|
||||||
pub {name_upper},
|
|
||||||
{category},
|
|
||||||
"default lint description"
|
|
||||||
}}
|
|
||||||
"#},
|
|
||||||
version = version,
|
|
||||||
name_upper = name_upper,
|
|
||||||
category = category,
|
|
||||||
);
|
|
||||||
|
|
||||||
result.push_str(&if enable_msrv {
|
result.push_str(&if enable_msrv {
|
||||||
format!(
|
format!(
|
||||||
@ -312,6 +311,254 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_lint_declaration(name_upper: &str, category: &str) -> String {
|
||||||
|
format!(
|
||||||
|
indoc! {r#"
|
||||||
|
declare_clippy_lint! {{
|
||||||
|
/// ### What it does
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// // example code where clippy issues a warning
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// // example code which does not raise clippy warning
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "{version}"]
|
||||||
|
pub {name_upper},
|
||||||
|
{category},
|
||||||
|
"default lint description"
|
||||||
|
}}
|
||||||
|
"#},
|
||||||
|
version = get_stabilization_version(),
|
||||||
|
name_upper = name_upper,
|
||||||
|
category = category,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::Result<()> {
|
||||||
|
match ty {
|
||||||
|
"cargo" => assert_eq!(
|
||||||
|
lint.category, "cargo",
|
||||||
|
"Lints of type `cargo` must have the `cargo` category"
|
||||||
|
),
|
||||||
|
_ if lint.category == "cargo" => panic!("Lints of category `cargo` must have the `cargo` type"),
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty_dir = lint.project_root.join(format!("clippy_lints/src/{}", ty));
|
||||||
|
assert!(
|
||||||
|
ty_dir.exists() && ty_dir.is_dir(),
|
||||||
|
"Directory `{}` does not exist!",
|
||||||
|
ty_dir.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
let lint_file_path = ty_dir.join(format!("{}.rs", lint.name));
|
||||||
|
assert!(
|
||||||
|
!lint_file_path.exists(),
|
||||||
|
"File `{}` already exists",
|
||||||
|
lint_file_path.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mod_file_path = ty_dir.join("mod.rs");
|
||||||
|
let context_import = setup_mod_file(&mod_file_path, lint)?;
|
||||||
|
|
||||||
|
let name_upper = lint.name.to_uppercase();
|
||||||
|
let mut lint_file_contents = String::new();
|
||||||
|
|
||||||
|
if enable_msrv {
|
||||||
|
let _ = writedoc!(
|
||||||
|
lint_file_contents,
|
||||||
|
r#"
|
||||||
|
use clippy_utils::{{meets_msrv, msrvs}};
|
||||||
|
use rustc_lint::{{{context_import}, LintContext}};
|
||||||
|
use rustc_semver::RustcVersion;
|
||||||
|
|
||||||
|
use super::{name_upper};
|
||||||
|
|
||||||
|
// TODO: Adjust the parameters as necessary
|
||||||
|
pub(super) fn check(cx: &{context_import}, msrv: Option<RustcVersion>) {{
|
||||||
|
if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
|
||||||
|
return;
|
||||||
|
}}
|
||||||
|
todo!();
|
||||||
|
}}
|
||||||
|
"#,
|
||||||
|
context_import = context_import,
|
||||||
|
name_upper = name_upper,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let _ = writedoc!(
|
||||||
|
lint_file_contents,
|
||||||
|
r#"
|
||||||
|
use rustc_lint::{{{context_import}, LintContext}};
|
||||||
|
|
||||||
|
use super::{name_upper};
|
||||||
|
|
||||||
|
// TODO: Adjust the parameters as necessary
|
||||||
|
pub(super) fn check(cx: &{context_import}) {{
|
||||||
|
todo!();
|
||||||
|
}}
|
||||||
|
"#,
|
||||||
|
context_import = context_import,
|
||||||
|
name_upper = name_upper,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_file(lint_file_path.as_path(), lint_file_contents)?;
|
||||||
|
println!("Generated lint file: `clippy_lints/src/{}/{}.rs`", ty, lint.name);
|
||||||
|
println!(
|
||||||
|
"Be sure to add a call to `{}::check` in `clippy_lints/src/{}/mod.rs`!",
|
||||||
|
lint.name, ty
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> {
|
||||||
|
use super::update_lints::{match_tokens, LintDeclSearchResult};
|
||||||
|
use rustc_lexer::TokenKind;
|
||||||
|
|
||||||
|
let lint_name_upper = lint.name.to_uppercase();
|
||||||
|
|
||||||
|
let mut file_contents = fs::read_to_string(path)?;
|
||||||
|
assert!(
|
||||||
|
!file_contents.contains(&lint_name_upper),
|
||||||
|
"Lint `{}` already defined in `{}`",
|
||||||
|
lint.name,
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut offset = 0usize;
|
||||||
|
let mut last_decl_curly_offset = None;
|
||||||
|
let mut lint_context = None;
|
||||||
|
|
||||||
|
let mut iter = rustc_lexer::tokenize(&file_contents).map(|t| {
|
||||||
|
let range = offset..offset + t.len;
|
||||||
|
offset = range.end;
|
||||||
|
|
||||||
|
LintDeclSearchResult {
|
||||||
|
token_kind: t.kind,
|
||||||
|
content: &file_contents[range.clone()],
|
||||||
|
range,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl
|
||||||
|
while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) {
|
||||||
|
let mut iter = iter
|
||||||
|
.by_ref()
|
||||||
|
.filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
|
||||||
|
|
||||||
|
match content {
|
||||||
|
"declare_clippy_lint" => {
|
||||||
|
// matches `!{`
|
||||||
|
match_tokens!(iter, Bang OpenBrace);
|
||||||
|
if let Some(LintDeclSearchResult { range, .. }) =
|
||||||
|
iter.find(|result| result.token_kind == TokenKind::CloseBrace)
|
||||||
|
{
|
||||||
|
last_decl_curly_offset = Some(range.end);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"impl" => {
|
||||||
|
let mut token = iter.next();
|
||||||
|
match token {
|
||||||
|
// matches <'foo>
|
||||||
|
Some(LintDeclSearchResult {
|
||||||
|
token_kind: TokenKind::Lt,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
match_tokens!(iter, Lifetime { .. } Gt);
|
||||||
|
token = iter.next();
|
||||||
|
},
|
||||||
|
None => break,
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(LintDeclSearchResult {
|
||||||
|
token_kind: TokenKind::Ident,
|
||||||
|
content,
|
||||||
|
..
|
||||||
|
}) = token
|
||||||
|
{
|
||||||
|
// Get the appropriate lint context struct
|
||||||
|
lint_context = match content {
|
||||||
|
"LateLintPass" => Some("LateContext"),
|
||||||
|
"EarlyLintPass" => Some("EarlyContext"),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(iter);
|
||||||
|
|
||||||
|
let last_decl_curly_offset =
|
||||||
|
last_decl_curly_offset.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display()));
|
||||||
|
let lint_context =
|
||||||
|
lint_context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display()));
|
||||||
|
|
||||||
|
// Add the lint declaration to `mod.rs`
|
||||||
|
file_contents.replace_range(
|
||||||
|
// Remove the trailing newline, which should always be present
|
||||||
|
last_decl_curly_offset..=last_decl_curly_offset,
|
||||||
|
&format!("\n\n{}", get_lint_declaration(&lint_name_upper, lint.category)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add the lint to `impl_lint_pass`/`declare_lint_pass`
|
||||||
|
let impl_lint_pass_start = file_contents.find("impl_lint_pass!").unwrap_or_else(|| {
|
||||||
|
file_contents
|
||||||
|
.find("declare_lint_pass!")
|
||||||
|
.unwrap_or_else(|| panic!("failed to find `impl_lint_pass`/`declare_lint_pass`"))
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut arr_start = file_contents[impl_lint_pass_start..].find('[').unwrap_or_else(|| {
|
||||||
|
panic!("malformed `impl_lint_pass`/`declare_lint_pass`");
|
||||||
|
});
|
||||||
|
|
||||||
|
arr_start += impl_lint_pass_start;
|
||||||
|
|
||||||
|
let mut arr_end = file_contents[arr_start..]
|
||||||
|
.find(']')
|
||||||
|
.expect("failed to find `impl_lint_pass` terminator");
|
||||||
|
|
||||||
|
arr_end += arr_start;
|
||||||
|
|
||||||
|
let mut arr_content = file_contents[arr_start + 1..arr_end].to_string();
|
||||||
|
arr_content.retain(|c| !c.is_whitespace());
|
||||||
|
|
||||||
|
let mut new_arr_content = String::new();
|
||||||
|
for ident in arr_content
|
||||||
|
.split(',')
|
||||||
|
.chain(std::iter::once(&*lint_name_upper))
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
{
|
||||||
|
let _ = write!(new_arr_content, "\n {},", ident);
|
||||||
|
}
|
||||||
|
new_arr_content.push('\n');
|
||||||
|
|
||||||
|
file_contents.replace_range(arr_start + 1..arr_end, &new_arr_content);
|
||||||
|
|
||||||
|
// Just add the mod declaration at the top, it'll be fixed by rustfmt
|
||||||
|
file_contents.insert_str(0, &format!("mod {};\n", &lint.name));
|
||||||
|
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(path)
|
||||||
|
.context(format!("trying to open: `{}`", path.display()))?;
|
||||||
|
file.write_all(file_contents.as_bytes())
|
||||||
|
.context(format!("writing to file: `{}`", path.display()))?;
|
||||||
|
|
||||||
|
Ok(lint_context)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_camel_case() {
|
fn test_camel_case() {
|
||||||
let s = "a_lint";
|
let s = "a_lint";
|
||||||
|
@ -824,10 +824,12 @@ macro_rules! match_tokens {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LintDeclSearchResult<'a> {
|
pub(crate) use match_tokens;
|
||||||
token_kind: TokenKind,
|
|
||||||
content: &'a str,
|
pub(crate) struct LintDeclSearchResult<'a> {
|
||||||
range: Range<usize>,
|
pub token_kind: TokenKind,
|
||||||
|
pub content: &'a str,
|
||||||
|
pub range: Range<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a source file looking for `declare_clippy_lint` macro invocations.
|
/// Parse a source file looking for `declare_clippy_lint` macro invocations.
|
||||||
|
@ -11,7 +11,7 @@ declare_clippy_lint! {
|
|||||||
/// Note that this lint is specialized in linting *every single* use of `as`
|
/// Note that this lint is specialized in linting *every single* use of `as`
|
||||||
/// regardless of whether good alternatives exist or not.
|
/// regardless of whether good alternatives exist or not.
|
||||||
/// If you want more precise lints for `as`, please consider using these separate lints:
|
/// If you want more precise lints for `as`, please consider using these separate lints:
|
||||||
/// `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,
|
/// `unnecessary_cast`, `cast_lossless/cast_possible_truncation/cast_possible_wrap/cast_precision_loss/cast_sign_loss`,
|
||||||
/// `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.
|
/// `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.
|
||||||
/// There is a good explanation the reason why this lint should work in this way and how it is useful
|
/// There is a good explanation the reason why this lint should work in this way and how it is useful
|
||||||
/// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).
|
/// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
|
||||||
|
use clippy_utils::path_res;
|
||||||
|
use clippy_utils::source::snippet_with_context;
|
||||||
|
use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item};
|
||||||
|
use clippy_utils::usage::local_used_after_expr;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::def::Res;
|
||||||
|
use rustc_hir::{Expr, ExprKind};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_middle::ty::{self, Ty};
|
||||||
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
|
use rustc_span::sym;
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for `assert!(r.is_ok())` or `assert!(r.is_err())` calls.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// An assertion failure cannot output an useful message of the error.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// # let r = Ok::<_, ()>(());
|
||||||
|
/// assert!(r.is_ok());
|
||||||
|
/// # let r = Err::<_, ()>(());
|
||||||
|
/// assert!(r.is_err());
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.64.0"]
|
||||||
|
pub ASSERTIONS_ON_RESULT_STATES,
|
||||||
|
style,
|
||||||
|
"`assert!(r.is_ok())`/`assert!(r.is_err())` gives worse error message than directly calling `r.unwrap()`/`r.unwrap_err()`"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_lint_pass!(AssertionsOnResultStates => [ASSERTIONS_ON_RESULT_STATES]);
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||||
|
if let Some(macro_call) = root_macro_call_first_node(cx, e)
|
||||||
|
&& matches!(cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::assert_macro))
|
||||||
|
&& let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn)
|
||||||
|
&& matches!(panic_expn, PanicExpn::Empty)
|
||||||
|
&& let ExprKind::MethodCall(method_segment, [recv], _) = condition.kind
|
||||||
|
&& let result_type_with_refs = cx.typeck_results().expr_ty(recv)
|
||||||
|
&& let result_type = result_type_with_refs.peel_refs()
|
||||||
|
&& is_type_diagnostic_item(cx, result_type, sym::Result)
|
||||||
|
&& let ty::Adt(_, substs) = result_type.kind()
|
||||||
|
{
|
||||||
|
if !is_copy(cx, result_type) {
|
||||||
|
if result_type_with_refs != result_type {
|
||||||
|
return;
|
||||||
|
} else if let Res::Local(binding_id) = path_res(cx, recv)
|
||||||
|
&& local_used_after_expr(cx, binding_id, recv) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut app = Applicability::MachineApplicable;
|
||||||
|
match method_segment.ident.as_str() {
|
||||||
|
"is_ok" if has_debug_impl(cx, substs.type_at(1)) => {
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
ASSERTIONS_ON_RESULT_STATES,
|
||||||
|
macro_call.span,
|
||||||
|
"called `assert!` with `Result::is_ok`",
|
||||||
|
"replace with",
|
||||||
|
format!(
|
||||||
|
"{}.unwrap()",
|
||||||
|
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
|
||||||
|
),
|
||||||
|
app,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"is_err" if has_debug_impl(cx, substs.type_at(0)) => {
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
ASSERTIONS_ON_RESULT_STATES,
|
||||||
|
macro_call.span,
|
||||||
|
"called `assert!` with `Result::is_err`",
|
||||||
|
"replace with",
|
||||||
|
format!(
|
||||||
|
"{}.unwrap_err()",
|
||||||
|
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
|
||||||
|
),
|
||||||
|
app,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This checks whether a given type is known to implement Debug.
|
||||||
|
fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||||
|
cx.tcx
|
||||||
|
.get_diagnostic_item(sym::Debug)
|
||||||
|
.map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
|
||||||
|
}
|
@ -1,3 +1,8 @@
|
|||||||
|
mod common_metadata;
|
||||||
|
mod feature_name;
|
||||||
|
mod multiple_crate_versions;
|
||||||
|
mod wildcard_dependencies;
|
||||||
|
|
||||||
use cargo_metadata::MetadataCommand;
|
use cargo_metadata::MetadataCommand;
|
||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::is_lint_allowed;
|
use clippy_utils::is_lint_allowed;
|
||||||
@ -6,11 +11,6 @@ use rustc_lint::{LateContext, LateLintPass, Lint};
|
|||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
|
|
||||||
mod common_metadata;
|
|
||||||
mod feature_name;
|
|
||||||
mod multiple_crate_versions;
|
|
||||||
mod wildcard_dependencies;
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks to see if all common metadata is defined in
|
/// Checks to see if all common metadata is defined in
|
||||||
|
@ -24,7 +24,10 @@ declare_clippy_lint! {
|
|||||||
/// Use instead:
|
/// Use instead:
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// fn is_rust_file(filename: &str) -> bool {
|
/// fn is_rust_file(filename: &str) -> bool {
|
||||||
/// filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true)
|
/// let filename = std::path::Path::new(filename);
|
||||||
|
/// filename.extension()
|
||||||
|
/// .map(|ext| ext.eq_ignore_ascii_case("rs"))
|
||||||
|
/// .unwrap_or(false)
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.51.0"]
|
#[clippy::version = "1.51.0"]
|
||||||
|
@ -1028,9 +1028,10 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
|
|||||||
let mut app = Applicability::MachineApplicable;
|
let mut app = Applicability::MachineApplicable;
|
||||||
let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
|
let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
|
||||||
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
|
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
|
||||||
|
let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
|
||||||
let sugg = if !snip_is_macro
|
let sugg = if !snip_is_macro
|
||||||
&& expr.precedence().order() < data.position.precedence()
|
|
||||||
&& !has_enclosing_paren(&snip)
|
&& !has_enclosing_paren(&snip)
|
||||||
|
&& (expr.precedence().order() < data.position.precedence() || calls_field)
|
||||||
{
|
{
|
||||||
format!("({})", snip)
|
format!("({})", snip)
|
||||||
} else {
|
} else {
|
||||||
|
@ -9,7 +9,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::symbol::kw;
|
use rustc_span::symbol::kw;
|
||||||
use rustc_span::{sym, BytePos, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
@ -85,22 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
|||||||
ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string,
|
ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
let sugg = if format_args.format_string_span.contains(value.span) {
|
let sugg = if is_new_string {
|
||||||
// Implicit argument. e.g. `format!("{x}")` span points to `{x}`
|
|
||||||
let spdata = value.span.data();
|
|
||||||
let span = Span::new(
|
|
||||||
spdata.lo + BytePos(1),
|
|
||||||
spdata.hi - BytePos(1),
|
|
||||||
spdata.ctxt,
|
|
||||||
spdata.parent
|
|
||||||
);
|
|
||||||
let snip = snippet_with_applicability(cx, span, "..", &mut applicability);
|
|
||||||
if is_new_string {
|
|
||||||
snip.into()
|
|
||||||
} else {
|
|
||||||
format!("{snip}.to_string()")
|
|
||||||
}
|
|
||||||
} else if is_new_string {
|
|
||||||
snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
|
snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
|
||||||
} else {
|
} else {
|
||||||
let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
|
let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
|
||||||
|
@ -141,7 +141,7 @@ fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||||||
// Get the hir_id of the object we are calling the method on
|
// Get the hir_id of the object we are calling the method on
|
||||||
if let ExprKind::MethodCall(path, [ref self_arg, ..], _) = expr.kind;
|
if let ExprKind::MethodCall(path, [ref self_arg, ..], _) = expr.kind;
|
||||||
// Is the method to_string() ?
|
// Is the method to_string() ?
|
||||||
if path.ident.name == sym!(to_string);
|
if path.ident.name == sym::to_string;
|
||||||
// Is the method a part of the ToString trait? (i.e. not to_string() implemented
|
// Is the method a part of the ToString trait? (i.e. not to_string() implemented
|
||||||
// separately)
|
// separately)
|
||||||
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||||
|
@ -6,6 +6,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||||||
LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
|
LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
|
||||||
LintId::of(approx_const::APPROX_CONSTANT),
|
LintId::of(approx_const::APPROX_CONSTANT),
|
||||||
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
|
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
|
||||||
|
LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
|
||||||
LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
|
LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
|
||||||
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
||||||
LintId::of(attrs::DEPRECATED_CFG_ATTR),
|
LintId::of(attrs::DEPRECATED_CFG_ATTR),
|
||||||
@ -187,6 +188,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||||||
LintId::of(methods::NEEDLESS_SPLITN),
|
LintId::of(methods::NEEDLESS_SPLITN),
|
||||||
LintId::of(methods::NEW_RET_NO_SELF),
|
LintId::of(methods::NEW_RET_NO_SELF),
|
||||||
LintId::of(methods::NO_EFFECT_REPLACE),
|
LintId::of(methods::NO_EFFECT_REPLACE),
|
||||||
|
LintId::of(methods::OBFUSCATED_IF_ELSE),
|
||||||
LintId::of(methods::OK_EXPECT),
|
LintId::of(methods::OK_EXPECT),
|
||||||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||||
LintId::of(methods::OPTION_FILTER_MAP),
|
LintId::of(methods::OPTION_FILTER_MAP),
|
||||||
|
@ -42,6 +42,7 @@ store.register_lints(&[
|
|||||||
asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
|
asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
|
||||||
asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
|
asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
|
||||||
assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
|
assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
|
||||||
|
assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES,
|
||||||
async_yields_async::ASYNC_YIELDS_ASYNC,
|
async_yields_async::ASYNC_YIELDS_ASYNC,
|
||||||
attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||||
attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
|
attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||||
@ -330,6 +331,7 @@ store.register_lints(&[
|
|||||||
methods::NEEDLESS_SPLITN,
|
methods::NEEDLESS_SPLITN,
|
||||||
methods::NEW_RET_NO_SELF,
|
methods::NEW_RET_NO_SELF,
|
||||||
methods::NO_EFFECT_REPLACE,
|
methods::NO_EFFECT_REPLACE,
|
||||||
|
methods::OBFUSCATED_IF_ELSE,
|
||||||
methods::OK_EXPECT,
|
methods::OK_EXPECT,
|
||||||
methods::OPTION_AS_REF_DEREF,
|
methods::OPTION_AS_REF_DEREF,
|
||||||
methods::OPTION_FILTER_MAP,
|
methods::OPTION_FILTER_MAP,
|
||||||
@ -417,6 +419,7 @@ store.register_lints(&[
|
|||||||
only_used_in_recursion::ONLY_USED_IN_RECURSION,
|
only_used_in_recursion::ONLY_USED_IN_RECURSION,
|
||||||
open_options::NONSENSICAL_OPEN_OPTIONS,
|
open_options::NONSENSICAL_OPEN_OPTIONS,
|
||||||
operators::ABSURD_EXTREME_COMPARISONS,
|
operators::ABSURD_EXTREME_COMPARISONS,
|
||||||
|
operators::ARITHMETIC,
|
||||||
operators::ASSIGN_OP_PATTERN,
|
operators::ASSIGN_OP_PATTERN,
|
||||||
operators::BAD_BIT_MASK,
|
operators::BAD_BIT_MASK,
|
||||||
operators::CMP_NAN,
|
operators::CMP_NAN,
|
||||||
|
@ -48,6 +48,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
|||||||
LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
|
LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
|
||||||
LintId::of(module_style::MOD_MODULE_FILES),
|
LintId::of(module_style::MOD_MODULE_FILES),
|
||||||
LintId::of(module_style::SELF_NAMED_MODULE_FILES),
|
LintId::of(module_style::SELF_NAMED_MODULE_FILES),
|
||||||
|
LintId::of(operators::ARITHMETIC),
|
||||||
LintId::of(operators::FLOAT_ARITHMETIC),
|
LintId::of(operators::FLOAT_ARITHMETIC),
|
||||||
LintId::of(operators::FLOAT_CMP_CONST),
|
LintId::of(operators::FLOAT_CMP_CONST),
|
||||||
LintId::of(operators::INTEGER_ARITHMETIC),
|
LintId::of(operators::INTEGER_ARITHMETIC),
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||||
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
|
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
|
||||||
|
LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
|
||||||
LintId::of(blacklisted_name::BLACKLISTED_NAME),
|
LintId::of(blacklisted_name::BLACKLISTED_NAME),
|
||||||
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||||
@ -70,6 +71,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||||||
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
||||||
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
|
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
|
||||||
LintId::of(methods::NEW_RET_NO_SELF),
|
LintId::of(methods::NEW_RET_NO_SELF),
|
||||||
|
LintId::of(methods::OBFUSCATED_IF_ELSE),
|
||||||
LintId::of(methods::OK_EXPECT),
|
LintId::of(methods::OK_EXPECT),
|
||||||
LintId::of(methods::OPTION_MAP_OR_NONE),
|
LintId::of(methods::OPTION_MAP_OR_NONE),
|
||||||
LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
|
LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
|
||||||
|
@ -174,6 +174,7 @@ mod as_conversions;
|
|||||||
mod as_underscore;
|
mod as_underscore;
|
||||||
mod asm_syntax;
|
mod asm_syntax;
|
||||||
mod assertions_on_constants;
|
mod assertions_on_constants;
|
||||||
|
mod assertions_on_result_states;
|
||||||
mod async_yields_async;
|
mod async_yields_async;
|
||||||
mod attrs;
|
mod attrs;
|
||||||
mod await_holding_invalid;
|
mod await_holding_invalid;
|
||||||
@ -548,6 +549,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
|
store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let arithmetic_allowed = conf.arithmetic_allowed.clone();
|
||||||
|
store.register_late_pass(move || Box::new(operators::arithmetic::Arithmetic::new(arithmetic_allowed.clone())));
|
||||||
store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir));
|
store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir));
|
||||||
store.register_late_pass(|| Box::new(utils::author::Author));
|
store.register_late_pass(|| Box::new(utils::author::Author));
|
||||||
let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
|
let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
|
||||||
@ -727,6 +730,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
|
store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
|
||||||
store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
|
store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
|
||||||
store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
|
store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
|
||||||
|
store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates));
|
||||||
store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
|
store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
|
||||||
store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
|
store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
|
||||||
store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
|
store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
|
||||||
@ -782,7 +786,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
))
|
))
|
||||||
});
|
});
|
||||||
store.register_late_pass(|| Box::new(default::Default::default()));
|
store.register_late_pass(|| Box::new(default::Default::default()));
|
||||||
store.register_late_pass(|| Box::new(unused_self::UnusedSelf));
|
store.register_late_pass(move || Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api)));
|
||||||
store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
|
store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
|
||||||
store.register_late_pass(|| Box::new(exit::Exit));
|
store.register_late_pass(|| Box::new(exit::Exit));
|
||||||
store.register_late_pass(|| Box::new(to_digit_is_some::ToDigitIsSome));
|
store.register_late_pass(|| Box::new(to_digit_is_some::ToDigitIsSome));
|
||||||
@ -916,7 +920,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
|
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
|
||||||
store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
|
store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
|
||||||
store.register_late_pass(|| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
|
store.register_late_pass(|| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
|
||||||
store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports));
|
store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
|
||||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,7 +460,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
|||||||
let mut sub_visitor = RefVisitor::new(self.cx);
|
let mut sub_visitor = RefVisitor::new(self.cx);
|
||||||
sub_visitor.visit_fn_decl(decl);
|
sub_visitor.visit_fn_decl(decl);
|
||||||
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
|
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
|
||||||
return;
|
|
||||||
},
|
},
|
||||||
TyKind::TraitObject(bounds, ref lt, _) => {
|
TyKind::TraitObject(bounds, ref lt, _) => {
|
||||||
if !lt.is_elided() {
|
if !lt.is_elided() {
|
||||||
@ -469,7 +468,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
|||||||
for bound in bounds {
|
for bound in bounds {
|
||||||
self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
|
self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
},
|
},
|
||||||
_ => walk_ty(self, ty),
|
_ => walk_ty(self, ty),
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,3 @@
|
|||||||
use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
|
|
||||||
use clippy_utils::{higher, in_constant, meets_msrv, msrvs};
|
|
||||||
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
|
|
||||||
use rustc_lexer::{tokenize, TokenKind};
|
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
|
||||||
use rustc_middle::lint::in_external_macro;
|
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
|
||||||
use rustc_span::{Span, SpanData, SyntaxContext};
|
|
||||||
|
|
||||||
mod collapsible_match;
|
mod collapsible_match;
|
||||||
mod infallible_destructuring_match;
|
mod infallible_destructuring_match;
|
||||||
mod manual_map;
|
mod manual_map;
|
||||||
@ -31,6 +21,16 @@ mod single_match;
|
|||||||
mod try_err;
|
mod try_err;
|
||||||
mod wild_in_or_pats;
|
mod wild_in_or_pats;
|
||||||
|
|
||||||
|
use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
|
||||||
|
use clippy_utils::{higher, in_constant, meets_msrv, msrvs};
|
||||||
|
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
|
||||||
|
use rustc_lexer::{tokenize, TokenKind};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
|
use rustc_middle::lint::in_external_macro;
|
||||||
|
use rustc_semver::RustcVersion;
|
||||||
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
|
use rustc_span::{Span, SpanData, SyntaxContext};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for matches with a single arm where an `if let`
|
/// Checks for matches with a single arm where an `if let`
|
||||||
@ -793,18 +793,13 @@ declare_clippy_lint! {
|
|||||||
/// ### Example
|
/// ### Example
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// # use std::sync::Mutex;
|
/// # use std::sync::Mutex;
|
||||||
///
|
|
||||||
/// # struct State {}
|
/// # struct State {}
|
||||||
///
|
|
||||||
/// # impl State {
|
/// # impl State {
|
||||||
/// # fn foo(&self) -> bool {
|
/// # fn foo(&self) -> bool {
|
||||||
/// # true
|
/// # true
|
||||||
/// # }
|
/// # }
|
||||||
///
|
|
||||||
/// # fn bar(&self) {}
|
/// # fn bar(&self) {}
|
||||||
/// # }
|
/// # }
|
||||||
///
|
|
||||||
///
|
|
||||||
/// let mutex = Mutex::new(State {});
|
/// let mutex = Mutex::new(State {});
|
||||||
///
|
///
|
||||||
/// match mutex.lock().unwrap().foo() {
|
/// match mutex.lock().unwrap().foo() {
|
||||||
@ -815,22 +810,17 @@ declare_clippy_lint! {
|
|||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// println!("All done!");
|
/// println!("All done!");
|
||||||
///
|
|
||||||
/// ```
|
/// ```
|
||||||
/// Use instead:
|
/// Use instead:
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use std::sync::Mutex;
|
/// # use std::sync::Mutex;
|
||||||
///
|
|
||||||
/// # struct State {}
|
/// # struct State {}
|
||||||
///
|
|
||||||
/// # impl State {
|
/// # impl State {
|
||||||
/// # fn foo(&self) -> bool {
|
/// # fn foo(&self) -> bool {
|
||||||
/// # true
|
/// # true
|
||||||
/// # }
|
/// # }
|
||||||
///
|
|
||||||
/// # fn bar(&self) {}
|
/// # fn bar(&self) {}
|
||||||
/// # }
|
/// # }
|
||||||
///
|
|
||||||
/// let mutex = Mutex::new(State {});
|
/// let mutex = Mutex::new(State {});
|
||||||
///
|
///
|
||||||
/// let is_foo = mutex.lock().unwrap().foo();
|
/// let is_foo = mutex.lock().unwrap().foo();
|
||||||
|
@ -14,7 +14,7 @@ use super::INEFFICIENT_TO_STRING;
|
|||||||
/// Checks for the `INEFFICIENT_TO_STRING` lint
|
/// Checks for the `INEFFICIENT_TO_STRING` lint
|
||||||
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
|
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if args.len() == 1 && method_name == sym!(to_string);
|
if args.len() == 1 && method_name == sym::to_string;
|
||||||
if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||||
if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
|
if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
|
||||||
if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id);
|
if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id);
|
||||||
|
@ -46,6 +46,7 @@ mod map_unwrap_or;
|
|||||||
mod needless_option_as_deref;
|
mod needless_option_as_deref;
|
||||||
mod needless_option_take;
|
mod needless_option_take;
|
||||||
mod no_effect_replace;
|
mod no_effect_replace;
|
||||||
|
mod obfuscated_if_else;
|
||||||
mod ok_expect;
|
mod ok_expect;
|
||||||
mod option_as_ref_deref;
|
mod option_as_ref_deref;
|
||||||
mod option_map_or_none;
|
mod option_map_or_none;
|
||||||
@ -2263,6 +2264,35 @@ declare_clippy_lint! {
|
|||||||
"replace with no effect"
|
"replace with no effect"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for usages of `.then_some(..).unwrap_or(..)`
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// This can be written more clearly with `if .. else ..`
|
||||||
|
///
|
||||||
|
/// ### Limitations
|
||||||
|
/// This lint currently only looks for usages of
|
||||||
|
/// `.then_some(..).unwrap_or(..)`, but will be expanded
|
||||||
|
/// to account for similar patterns.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// let x = true;
|
||||||
|
/// x.then_some("a").unwrap_or("b");
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// let x = true;
|
||||||
|
/// if x { "a" } else { "b" };
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.64.0"]
|
||||||
|
pub OBFUSCATED_IF_ELSE,
|
||||||
|
style,
|
||||||
|
"use of `.then_some(..).unwrap_or(..)` can be written \
|
||||||
|
more clearly with `if .. else ..`"
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Methods {
|
pub struct Methods {
|
||||||
avoid_breaking_exported_api: bool,
|
avoid_breaking_exported_api: bool,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Option<RustcVersion>,
|
||||||
@ -2364,6 +2394,7 @@ impl_lint_pass!(Methods => [
|
|||||||
IS_DIGIT_ASCII_RADIX,
|
IS_DIGIT_ASCII_RADIX,
|
||||||
NEEDLESS_OPTION_TAKE,
|
NEEDLESS_OPTION_TAKE,
|
||||||
NO_EFFECT_REPLACE,
|
NO_EFFECT_REPLACE,
|
||||||
|
OBFUSCATED_IF_ELSE,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/// Extracts a method call name, args, and `Span` of the method name.
|
/// Extracts a method call name, args, and `Span` of the method name.
|
||||||
@ -2772,6 +2803,9 @@ impl Methods {
|
|||||||
Some(("map", [m_recv, m_arg], span)) => {
|
Some(("map", [m_recv, m_arg], span)) => {
|
||||||
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
|
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
|
||||||
},
|
},
|
||||||
|
Some(("then_some", [t_recv, t_arg], _)) => {
|
||||||
|
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
},
|
},
|
||||||
("unwrap_or_else", [u_arg]) => match method_call(recv) {
|
("unwrap_or_else", [u_arg]) => match method_call(recv) {
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
use super::OBFUSCATED_IF_ELSE;
|
||||||
|
use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_with_applicability};
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
|
||||||
|
pub(super) fn check<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
expr: &'tcx hir::Expr<'_>,
|
||||||
|
then_recv: &'tcx hir::Expr<'_>,
|
||||||
|
then_arg: &'tcx hir::Expr<'_>,
|
||||||
|
unwrap_arg: &'tcx hir::Expr<'_>,
|
||||||
|
) {
|
||||||
|
// something.then_some(blah).unwrap_or(blah)
|
||||||
|
// ^^^^^^^^^-then_recv ^^^^-then_arg ^^^^- unwrap_arg
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr
|
||||||
|
|
||||||
|
let recv_ty = cx.typeck_results().expr_ty(then_recv);
|
||||||
|
|
||||||
|
if recv_ty.is_bool() {
|
||||||
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
let sugg = format!(
|
||||||
|
"if {} {{ {} }} else {{ {} }}",
|
||||||
|
snippet_with_applicability(cx, then_recv.span, "..", &mut applicability),
|
||||||
|
snippet_with_applicability(cx, then_arg.span, "..", &mut applicability),
|
||||||
|
snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability)
|
||||||
|
);
|
||||||
|
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
OBFUSCATED_IF_ELSE,
|
||||||
|
expr.span,
|
||||||
|
"use of `.then_some(..).unwrap_or(..)` can be written \
|
||||||
|
more clearly with `if .. else ..`",
|
||||||
|
"try",
|
||||||
|
sugg,
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,11 @@ declare_clippy_lint! {
|
|||||||
/// Naming type parameters inconsistently may cause you to refer to the
|
/// Naming type parameters inconsistently may cause you to refer to the
|
||||||
/// wrong type parameter.
|
/// wrong type parameter.
|
||||||
///
|
///
|
||||||
|
/// ### Limitations
|
||||||
|
/// This lint only applies to impl blocks with simple generic params, e.g.
|
||||||
|
/// `A`. If there is anything more complicated, such as a tuple, it will be
|
||||||
|
/// ignored.
|
||||||
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// struct Foo<A, B> {
|
/// struct Foo<A, B> {
|
||||||
@ -53,14 +58,15 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
|
|||||||
if !generic_args.args.is_empty();
|
if !generic_args.args.is_empty();
|
||||||
then {
|
then {
|
||||||
// get the name and span of the generic parameters in the Impl
|
// get the name and span of the generic parameters in the Impl
|
||||||
let impl_params = generic_args.args.iter()
|
let mut impl_params = Vec::new();
|
||||||
.filter_map(|p|
|
for p in generic_args.args.iter() {
|
||||||
match p {
|
match p {
|
||||||
GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
|
GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
|
||||||
Some((path.segments[0].ident.to_string(), path.span)),
|
impl_params.push((path.segments[0].ident.to_string(), path.span)),
|
||||||
_ => None,
|
GenericArg::Type(_) => return,
|
||||||
}
|
_ => (),
|
||||||
);
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// find the type that the Impl is for
|
// find the type that the Impl is for
|
||||||
// only lint on struct/enum/union for now
|
// only lint on struct/enum/union for now
|
||||||
@ -83,8 +89,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
|
|||||||
type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect();
|
type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect();
|
||||||
|
|
||||||
let type_name = segment.ident;
|
let type_name = segment.ident;
|
||||||
for (i, (impl_param_name, impl_param_span)) in impl_params.enumerate() {
|
for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() {
|
||||||
if mismatch_param_name(i, &impl_param_name, &type_param_names_hashmap) {
|
if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) {
|
||||||
let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order",
|
let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order",
|
||||||
type_name, impl_param_name);
|
type_name, impl_param_name);
|
||||||
let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params",
|
let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params",
|
||||||
@ -92,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
|
|||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
cx,
|
cx,
|
||||||
MISMATCHING_TYPE_PARAM_ORDER,
|
MISMATCHING_TYPE_PARAM_ORDER,
|
||||||
impl_param_span,
|
*impl_param_span,
|
||||||
&msg,
|
&msg,
|
||||||
None,
|
None,
|
||||||
&help
|
&help
|
||||||
|
@ -251,14 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
|||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
|
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
|
||||||
if let ItemKind::Const(hir_ty, body_id) = it.kind {
|
if let ItemKind::Const(hir_ty, body_id) = it.kind {
|
||||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
||||||
if !macro_backtrace(it.span).last().map_or(false, |macro_call| {
|
if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
|
||||||
matches!(
|
|
||||||
cx.tcx.get_diagnostic_name(macro_call.def_id),
|
|
||||||
Some(sym::thread_local_macro)
|
|
||||||
)
|
|
||||||
}) && is_unfrozen(cx, ty)
|
|
||||||
&& is_value_unfrozen_poly(cx, body_id, ty)
|
|
||||||
{
|
|
||||||
lint(cx, Source::Item { item: it.span });
|
lint(cx, Source::Item { item: it.span });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -445,3 +438,12 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ignored_macro(cx: &LateContext<'_>, it: &rustc_hir::Item<'_>) -> bool {
|
||||||
|
macro_backtrace(it.span).any(|macro_call| {
|
||||||
|
matches!(
|
||||||
|
cx.tcx.get_diagnostic_name(macro_call.def_id),
|
||||||
|
Some(sym::thread_local_macro)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
119
src/tools/clippy/clippy_lints/src/operators/arithmetic.rs
Normal file
119
src/tools/clippy/clippy_lints/src/operators/arithmetic.rs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
#![allow(
|
||||||
|
// False positive
|
||||||
|
clippy::match_same_arms
|
||||||
|
)]
|
||||||
|
|
||||||
|
use super::ARITHMETIC;
|
||||||
|
use clippy_utils::{consts::constant_simple, diagnostics::span_lint};
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_session::impl_lint_pass;
|
||||||
|
use rustc_span::source_map::Span;
|
||||||
|
|
||||||
|
const HARD_CODED_ALLOWED: &[&str] = &["std::num::Saturating", "std::string::String", "std::num::Wrapping"];
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Arithmetic {
|
||||||
|
allowed: FxHashSet<String>,
|
||||||
|
// Used to check whether expressions are constants, such as in enum discriminants and consts
|
||||||
|
const_span: Option<Span>,
|
||||||
|
expr_span: Option<Span>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_lint_pass!(Arithmetic => [ARITHMETIC]);
|
||||||
|
|
||||||
|
impl Arithmetic {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(mut allowed: FxHashSet<String>) -> Self {
|
||||||
|
allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from));
|
||||||
|
Self {
|
||||||
|
allowed,
|
||||||
|
const_span: None,
|
||||||
|
expr_span: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given `expr` has any of the inner `allowed` elements.
|
||||||
|
fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||||
|
self.allowed.contains(
|
||||||
|
cx.typeck_results()
|
||||||
|
.expr_ty(expr)
|
||||||
|
.to_string()
|
||||||
|
.split('<')
|
||||||
|
.next()
|
||||||
|
.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
|
||||||
|
span_lint(cx, ARITHMETIC, expr.span, "arithmetic detected");
|
||||||
|
self.expr_span = Some(expr.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for Arithmetic {
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||||
|
if self.expr_span.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(span) = self.const_span && span.contains(expr.span) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
match &expr.kind {
|
||||||
|
hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||||
|
let (
|
||||||
|
hir::BinOpKind::Add
|
||||||
|
| hir::BinOpKind::Sub
|
||||||
|
| hir::BinOpKind::Mul
|
||||||
|
| hir::BinOpKind::Div
|
||||||
|
| hir::BinOpKind::Rem
|
||||||
|
| hir::BinOpKind::Shl
|
||||||
|
| hir::BinOpKind::Shr
|
||||||
|
) = op.node else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.issue_lint(cx, expr);
|
||||||
|
},
|
||||||
|
hir::ExprKind::Unary(hir::UnOp::Neg, _) => {
|
||||||
|
// CTFE already takes care of things like `-1` that do not overflow.
|
||||||
|
if constant_simple(cx, cx.typeck_results(), expr).is_none() {
|
||||||
|
self.issue_lint(cx, expr);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
|
||||||
|
let body_owner = cx.tcx.hir().body_owner_def_id(body.id());
|
||||||
|
match cx.tcx.hir().body_owner_kind(body_owner) {
|
||||||
|
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => {
|
||||||
|
let body_span = cx.tcx.def_span(body_owner);
|
||||||
|
if let Some(span) = self.const_span && span.contains(body_span) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.const_span = Some(body_span);
|
||||||
|
},
|
||||||
|
hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
|
||||||
|
let body_owner = cx.tcx.hir().body_owner(body.id());
|
||||||
|
let body_span = cx.tcx.hir().span(body_owner);
|
||||||
|
if let Some(span) = self.const_span && span.contains(body_span) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.const_span = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||||
|
if Some(expr.span) == self.expr_span {
|
||||||
|
self.expr_span = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,10 @@ use rustc_errors::Applicability;
|
|||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_middle::mir::FakeReadCause;
|
||||||
|
use rustc_middle::ty::BorrowKind;
|
||||||
|
use rustc_trait_selection::infer::TyCtxtInferExt;
|
||||||
|
use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
|
||||||
|
|
||||||
use super::ASSIGN_OP_PATTERN;
|
use super::ASSIGN_OP_PATTERN;
|
||||||
|
|
||||||
@ -29,6 +33,16 @@ pub(super) fn check<'tcx>(
|
|||||||
.map_or(true, |t| t.path.res.def_id() != trait_id);
|
.map_or(true, |t| t.path.res.def_id() != trait_id);
|
||||||
if implements_trait(cx, ty, trait_id, &[rty.into()]);
|
if implements_trait(cx, ty, trait_id, &[rty.into()]);
|
||||||
then {
|
then {
|
||||||
|
// Primitive types execute assign-ops right-to-left. Every other type is left-to-right.
|
||||||
|
if !(ty.is_primitive() && rty.is_primitive()) {
|
||||||
|
// TODO: This will have false negatives as it doesn't check if the borrows are
|
||||||
|
// actually live at the end of their respective expressions.
|
||||||
|
let mut_borrows = mut_borrows_in_expr(cx, assignee);
|
||||||
|
let imm_borrows = imm_borrows_in_expr(cx, rhs);
|
||||||
|
if mut_borrows.iter().any(|id| imm_borrows.contains(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
ASSIGN_OP_PATTERN,
|
ASSIGN_OP_PATTERN,
|
||||||
@ -99,3 +113,69 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
|
|||||||
walk_expr(self, expr);
|
walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet {
|
||||||
|
struct S(hir::HirIdSet);
|
||||||
|
impl Delegate<'_> for S {
|
||||||
|
fn borrow(&mut self, place: &PlaceWithHirId<'_>, _: hir::HirId, kind: BorrowKind) {
|
||||||
|
if matches!(kind, BorrowKind::ImmBorrow | BorrowKind::UniqueImmBorrow) {
|
||||||
|
self.0.insert(match place.place.base {
|
||||||
|
PlaceBase::Local(id) => id,
|
||||||
|
PlaceBase::Upvar(id) => id.var_path.hir_id,
|
||||||
|
_ => return,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {}
|
||||||
|
fn mutate(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {}
|
||||||
|
fn fake_read(&mut self, _: &PlaceWithHirId<'_>, _: FakeReadCause, _: hir::HirId) {}
|
||||||
|
fn copy(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut s = S(hir::HirIdSet::default());
|
||||||
|
cx.tcx.infer_ctxt().enter(|infcx| {
|
||||||
|
let mut v = ExprUseVisitor::new(
|
||||||
|
&mut s,
|
||||||
|
&infcx,
|
||||||
|
cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
|
||||||
|
cx.param_env,
|
||||||
|
cx.typeck_results(),
|
||||||
|
);
|
||||||
|
v.consume_expr(e);
|
||||||
|
});
|
||||||
|
s.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet {
|
||||||
|
struct S(hir::HirIdSet);
|
||||||
|
impl Delegate<'_> for S {
|
||||||
|
fn borrow(&mut self, place: &PlaceWithHirId<'_>, _: hir::HirId, kind: BorrowKind) {
|
||||||
|
if matches!(kind, BorrowKind::MutBorrow) {
|
||||||
|
self.0.insert(match place.place.base {
|
||||||
|
PlaceBase::Local(id) => id,
|
||||||
|
PlaceBase::Upvar(id) => id.var_path.hir_id,
|
||||||
|
_ => return,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {}
|
||||||
|
fn mutate(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {}
|
||||||
|
fn fake_read(&mut self, _: &PlaceWithHirId<'_>, _: FakeReadCause, _: hir::HirId) {}
|
||||||
|
fn copy(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut s = S(hir::HirIdSet::default());
|
||||||
|
cx.tcx.infer_ctxt().enter(|infcx| {
|
||||||
|
let mut v = ExprUseVisitor::new(
|
||||||
|
&mut s,
|
||||||
|
&infcx,
|
||||||
|
cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
|
||||||
|
cx.param_env,
|
||||||
|
cx.typeck_results(),
|
||||||
|
);
|
||||||
|
v.consume_expr(e);
|
||||||
|
});
|
||||||
|
s.0
|
||||||
|
}
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
use rustc_hir::{Body, Expr, ExprKind, UnOp};
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
|
||||||
|
|
||||||
mod absurd_extreme_comparisons;
|
mod absurd_extreme_comparisons;
|
||||||
mod assign_op_pattern;
|
mod assign_op_pattern;
|
||||||
mod bit_mask;
|
mod bit_mask;
|
||||||
@ -25,6 +21,12 @@ mod ptr_eq;
|
|||||||
mod self_assignment;
|
mod self_assignment;
|
||||||
mod verbose_bit_mask;
|
mod verbose_bit_mask;
|
||||||
|
|
||||||
|
pub(crate) mod arithmetic;
|
||||||
|
|
||||||
|
use rustc_hir::{Body, Expr, ExprKind, UnOp};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for comparisons where one side of the relation is
|
/// Checks for comparisons where one side of the relation is
|
||||||
@ -57,6 +59,42 @@ declare_clippy_lint! {
|
|||||||
"a comparison with a maximum or minimum value that is always true or false"
|
"a comparison with a maximum or minimum value that is always true or false"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for any kind of arithmetic operation of any type.
|
||||||
|
///
|
||||||
|
/// Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing according to the [Rust
|
||||||
|
/// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
|
||||||
|
/// or can panic (`/`, `%`). Known safe built-in types like `Wrapping` or `Saturing` are filtered
|
||||||
|
/// away.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// Integer overflow will trigger a panic in debug builds or will wrap in
|
||||||
|
/// release mode. Division by zero will cause a panic in either mode. In some applications one
|
||||||
|
/// wants explicitly checked, wrapping or saturating arithmetic.
|
||||||
|
///
|
||||||
|
/// #### Example
|
||||||
|
/// ```rust
|
||||||
|
/// # let a = 0;
|
||||||
|
/// a + 1;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Third-party types also tend to overflow.
|
||||||
|
///
|
||||||
|
/// #### Example
|
||||||
|
/// ```ignore,rust
|
||||||
|
/// use rust_decimal::Decimal;
|
||||||
|
/// let _n = Decimal::MAX + Decimal::MAX;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### Allowed types
|
||||||
|
/// Custom allowed types can be specified through the "arithmetic-allowed" filter.
|
||||||
|
#[clippy::version = "1.64.0"]
|
||||||
|
pub ARITHMETIC,
|
||||||
|
restriction,
|
||||||
|
"any arithmetic expression that could overflow or panic"
|
||||||
|
}
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for integer arithmetic operations which could overflow or panic.
|
/// Checks for integer arithmetic operations which could overflow or panic.
|
||||||
@ -747,6 +785,7 @@ pub struct Operators {
|
|||||||
}
|
}
|
||||||
impl_lint_pass!(Operators => [
|
impl_lint_pass!(Operators => [
|
||||||
ABSURD_EXTREME_COMPARISONS,
|
ABSURD_EXTREME_COMPARISONS,
|
||||||
|
ARITHMETIC,
|
||||||
INTEGER_ARITHMETIC,
|
INTEGER_ARITHMETIC,
|
||||||
FLOAT_ARITHMETIC,
|
FLOAT_ARITHMETIC,
|
||||||
ASSIGN_OP_PATTERN,
|
ASSIGN_OP_PATTERN,
|
||||||
|
@ -123,8 +123,8 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr:
|
|||||||
if_chain! {
|
if_chain! {
|
||||||
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
|
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
|
||||||
if !is_else_clause(cx.tcx, expr);
|
if !is_else_clause(cx.tcx, expr);
|
||||||
if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
|
if let PatKind::TupleStruct(ref path1, [field], None) = let_pat.kind;
|
||||||
if let PatKind::Binding(annot, bind_id, ident, _) = fields[0].kind;
|
if let PatKind::Binding(annot, bind_id, ident, _) = field.kind;
|
||||||
let caller_ty = cx.typeck_results().expr_ty(let_expr);
|
let caller_ty = cx.typeck_results().expr_ty(let_expr);
|
||||||
let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else);
|
let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else);
|
||||||
if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
|
if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use clippy_utils::diagnostics::span_lint_and_help;
|
use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
use rustc_hir::{def::Res, HirId, Path, PathSegment};
|
use rustc_hir::{def::Res, HirId, Path, PathSegment};
|
||||||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::{sym, symbol::kw, Symbol};
|
use rustc_span::{sym, symbol::kw, Span};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
@ -63,7 +63,7 @@ declare_clippy_lint! {
|
|||||||
/// ### Why is this bad?
|
/// ### Why is this bad?
|
||||||
///
|
///
|
||||||
/// Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are
|
/// Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are
|
||||||
/// imported from alloc to ensure disabling `alloc` does not cause the crate to fail to compile. This lint
|
/// imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint
|
||||||
/// is also useful for crates migrating to become `no_std` compatible.
|
/// is also useful for crates migrating to become `no_std` compatible.
|
||||||
///
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
@ -81,39 +81,55 @@ declare_clippy_lint! {
|
|||||||
"type is imported from alloc when available in core"
|
"type is imported from alloc when available in core"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
|
#[derive(Default)]
|
||||||
|
pub struct StdReexports {
|
||||||
|
// Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen
|
||||||
|
// twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
|
||||||
|
// when the path could be also be used to access the module.
|
||||||
|
prev_span: Span,
|
||||||
|
}
|
||||||
|
impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for StdReexports {
|
impl<'tcx> LateLintPass<'tcx> for StdReexports {
|
||||||
fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
|
fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
|
||||||
// std_instead_of_core
|
if let Res::Def(_, def_id) = path.res
|
||||||
check_path(cx, path, sym::std, sym::core, STD_INSTEAD_OF_CORE);
|
&& let Some(first_segment) = get_first_segment(path)
|
||||||
// std_instead_of_alloc
|
{
|
||||||
check_path(cx, path, sym::std, sym::alloc, STD_INSTEAD_OF_ALLOC);
|
let (lint, msg, help) = match first_segment.ident.name {
|
||||||
// alloc_instead_of_core
|
sym::std => match cx.tcx.crate_name(def_id.krate) {
|
||||||
check_path(cx, path, sym::alloc, sym::core, ALLOC_INSTEAD_OF_CORE);
|
sym::core => (
|
||||||
}
|
STD_INSTEAD_OF_CORE,
|
||||||
}
|
"used import from `std` instead of `core`",
|
||||||
|
"consider importing the item from `core`",
|
||||||
fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_crate: Symbol, lint: &'static Lint) {
|
),
|
||||||
if_chain! {
|
sym::alloc => (
|
||||||
// check if path resolves to the suggested crate.
|
STD_INSTEAD_OF_ALLOC,
|
||||||
if let Res::Def(_, def_id) = path.res;
|
"used import from `std` instead of `alloc`",
|
||||||
if suggested_crate == cx.tcx.crate_name(def_id.krate);
|
"consider importing the item from `alloc`",
|
||||||
|
),
|
||||||
// check if the first segment of the path is the crate we want to identify
|
_ => {
|
||||||
if let Some(path_root_segment) = get_first_segment(path);
|
self.prev_span = path.span;
|
||||||
|
return;
|
||||||
// check if the path matches the crate we want to suggest the other path for.
|
},
|
||||||
if krate == path_root_segment.ident.name;
|
},
|
||||||
then {
|
sym::alloc => {
|
||||||
span_lint_and_help(
|
if cx.tcx.crate_name(def_id.krate) == sym::core {
|
||||||
cx,
|
(
|
||||||
lint,
|
ALLOC_INSTEAD_OF_CORE,
|
||||||
path.span,
|
"used import from `alloc` instead of `core`",
|
||||||
&format!("used import from `{}` instead of `{}`", krate, suggested_crate),
|
"consider importing the item from `core`",
|
||||||
None,
|
)
|
||||||
&format!("consider importing the item from `{}`", suggested_crate),
|
} else {
|
||||||
);
|
self.prev_span = path.span;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
if path.span != self.prev_span {
|
||||||
|
span_lint_and_help(cx, lint, path.span, msg, None, help);
|
||||||
|
self.prev_span = path.span;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,12 +139,10 @@ fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_cr
|
|||||||
/// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
|
/// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
|
||||||
/// is returned.
|
/// is returned.
|
||||||
fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
|
fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
|
||||||
let segment = path.segments.first()?;
|
match path.segments {
|
||||||
|
// A global path will have PathRoot as the first segment. In this case, return the segment after.
|
||||||
// A global path will have PathRoot as the first segment. In this case, return the segment after.
|
[x, y, ..] if x.ident.name == kw::PathRoot => Some(y),
|
||||||
if segment.ident.name == kw::PathRoot {
|
[x, ..] => Some(x),
|
||||||
path.segments.get(1)
|
_ => None,
|
||||||
} else {
|
|
||||||
Some(segment)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -394,7 +394,7 @@ impl<'tcx> LateLintPass<'tcx> for StrToString {
|
|||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
|
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
|
||||||
if path.ident.name == sym!(to_string);
|
if path.ident.name == sym::to_string;
|
||||||
let ty = cx.typeck_results().expr_ty(self_arg);
|
let ty = cx.typeck_results().expr_ty(self_arg);
|
||||||
if let ty::Ref(_, ty, ..) = ty.kind();
|
if let ty::Ref(_, ty, ..) = ty.kind();
|
||||||
if *ty.kind() == ty::Str;
|
if *ty.kind() == ty::Str;
|
||||||
@ -444,7 +444,7 @@ impl<'tcx> LateLintPass<'tcx> for StringToString {
|
|||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
|
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
|
||||||
if path.ident.name == sym!(to_string);
|
if path.ident.name == sym::to_string;
|
||||||
let ty = cx.typeck_results().expr_ty(self_arg);
|
let ty = cx.typeck_results().expr_ty(self_arg);
|
||||||
if is_type_diagnostic_item(cx, ty, sym::String);
|
if is_type_diagnostic_item(cx, ty, sym::String);
|
||||||
then {
|
then {
|
||||||
|
@ -3,7 +3,7 @@ use clippy_utils::visitors::is_local_used;
|
|||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind};
|
use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
@ -35,7 +35,19 @@ declare_clippy_lint! {
|
|||||||
"methods that contain a `self` argument but don't use it"
|
"methods that contain a `self` argument but don't use it"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_lint_pass!(UnusedSelf => [UNUSED_SELF]);
|
pub struct UnusedSelf {
|
||||||
|
avoid_breaking_exported_api: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_lint_pass!(UnusedSelf => [UNUSED_SELF]);
|
||||||
|
|
||||||
|
impl UnusedSelf {
|
||||||
|
pub fn new(avoid_breaking_exported_api: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
avoid_breaking_exported_api,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
|
impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
|
||||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'_>) {
|
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'_>) {
|
||||||
@ -49,6 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
|
|||||||
if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind;
|
if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind;
|
||||||
if assoc_item.fn_has_self_parameter;
|
if assoc_item.fn_has_self_parameter;
|
||||||
if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
|
if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
|
||||||
|
if !cx.access_levels.is_exported(impl_item.def_id) || !self.avoid_breaking_exported_api;
|
||||||
let body = cx.tcx.hir().body(*body_id);
|
let body = cx.tcx.hir().body(*body_id);
|
||||||
if let [self_param, ..] = body.params;
|
if let [self_param, ..] = body.params;
|
||||||
if !is_local_used(cx, body, self_param.pat.hir_id);
|
if !is_local_used(cx, body, self_param.pat.hir_id);
|
||||||
|
@ -191,7 +191,11 @@ macro_rules! define_Conf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
define_Conf! {
|
define_Conf! {
|
||||||
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
|
/// Lint: Arithmetic.
|
||||||
|
///
|
||||||
|
/// Suppress checking of the passed type names.
|
||||||
|
(arithmetic_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
|
||||||
|
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
|
||||||
///
|
///
|
||||||
/// Suppress lints whenever the suggested change would cause breakage for other crates.
|
/// Suppress lints whenever the suggested change would cause breakage for other crates.
|
||||||
(avoid_breaking_exported_api: bool = true),
|
(avoid_breaking_exported_api: bool = true),
|
||||||
|
@ -619,32 +619,24 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -
|
|||||||
},
|
},
|
||||||
mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() {
|
mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() {
|
||||||
ty::Array(sub_type, len) => match sub_type.kind() {
|
ty::Array(sub_type, len) => match sub_type.kind() {
|
||||||
ty::Float(FloatTy::F32) => match len.to_valtree().try_to_machine_usize(tcx) {
|
ty::Float(FloatTy::F32) => match len.kind().try_to_machine_usize(tcx) {
|
||||||
Some(len) => alloc
|
Some(len) => alloc
|
||||||
.inner()
|
.inner()
|
||||||
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
|
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.chunks(4)
|
.array_chunks::<4>()
|
||||||
.map(|chunk| {
|
.map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
|
||||||
Some(Constant::F32(f32::from_le_bytes(
|
|
||||||
chunk.try_into().expect("this shouldn't happen"),
|
|
||||||
)))
|
|
||||||
})
|
|
||||||
.collect::<Option<Vec<Constant>>>()
|
.collect::<Option<Vec<Constant>>>()
|
||||||
.map(Constant::Vec),
|
.map(Constant::Vec),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
ty::Float(FloatTy::F64) => match len.to_valtree().try_to_machine_usize(tcx) {
|
ty::Float(FloatTy::F64) => match len.kind().try_to_machine_usize(tcx) {
|
||||||
Some(len) => alloc
|
Some(len) => alloc
|
||||||
.inner()
|
.inner()
|
||||||
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
|
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.chunks(8)
|
.array_chunks::<8>()
|
||||||
.map(|chunk| {
|
.map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
|
||||||
Some(Constant::F64(f64::from_le_bytes(
|
|
||||||
chunk.try_into().expect("this shouldn't happen"),
|
|
||||||
)))
|
|
||||||
})
|
|
||||||
.collect::<Option<Vec<Constant>>>()
|
.collect::<Option<Vec<Constant>>>()
|
||||||
.map(Constant::Vec),
|
.map(Constant::Vec),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -127,9 +127,6 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||||||
|
|
||||||
/// Checks whether two blocks are the same.
|
/// Checks whether two blocks are the same.
|
||||||
fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
|
fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
|
||||||
if self.cannot_be_compared_block(left) || self.cannot_be_compared_block(right) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
match (left.stmts, left.expr, right.stmts, right.expr) {
|
match (left.stmts, left.expr, right.stmts, right.expr) {
|
||||||
([], None, [], None) => {
|
([], None, [], None) => {
|
||||||
// For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
|
// For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
|
||||||
@ -180,36 +177,13 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cannot_be_compared_block(&mut self, block: &Block<'_>) -> bool {
|
|
||||||
if block.stmts.last().map_or(false, |stmt| {
|
|
||||||
matches!(
|
|
||||||
stmt.kind,
|
|
||||||
StmtKind::Semi(semi_expr) if self.should_ignore(semi_expr)
|
|
||||||
)
|
|
||||||
}) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(block_expr) = block.expr
|
|
||||||
&& self.should_ignore(block_expr)
|
|
||||||
{
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
|
fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
|
||||||
if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
|
macro_backtrace(expr.span).last().map_or(false, |macro_call| {
|
||||||
matches!(
|
matches!(
|
||||||
&self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
|
&self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
|
||||||
Some(sym::todo_macro | sym::unimplemented_macro)
|
Some(sym::todo_macro | sym::unimplemented_macro)
|
||||||
)
|
)
|
||||||
}) {
|
})
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool {
|
pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool {
|
||||||
@ -327,7 +301,8 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||||||
(&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
|
(&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
is_eq || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
|
(is_eq && (!self.should_ignore(left) || !self.should_ignore(right)))
|
||||||
|
|| self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
|
fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#![feature(array_chunks)]
|
||||||
#![feature(box_patterns)]
|
#![feature(box_patterns)]
|
||||||
#![feature(control_flow_enum)]
|
#![feature(control_flow_enum)]
|
||||||
#![feature(let_else)]
|
#![feature(let_else)]
|
||||||
@ -2141,7 +2142,7 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
|
|||||||
|
|
||||||
static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
|
static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
|
||||||
|
|
||||||
fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
|
fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
|
||||||
let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
|
let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
|
||||||
let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
|
let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
|
||||||
let value = map.entry(module);
|
let value = map.entry(module);
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly-2022-07-15"
|
channel = "nightly-2022-07-28"
|
||||||
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||||
|
@ -433,7 +433,7 @@ fn rustfix_coverage_known_exceptions_accuracy() {
|
|||||||
let rs_path = Path::new("tests/ui").join(filename);
|
let rs_path = Path::new("tests/ui").join(filename);
|
||||||
assert!(
|
assert!(
|
||||||
rs_path.exists(),
|
rs_path.exists(),
|
||||||
"`{}` does not exists",
|
"`{}` does not exist",
|
||||||
rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
|
rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
|
||||||
);
|
);
|
||||||
let fixed_path = rs_path.with_extension("fixed");
|
let fixed_path = rs_path.with_extension("fixed");
|
||||||
@ -445,6 +445,45 @@ fn rustfix_coverage_known_exceptions_accuracy() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ui_cargo_toml_metadata() {
|
||||||
|
let ui_cargo_path = Path::new("tests/ui-cargo");
|
||||||
|
let cargo_common_metadata_path = ui_cargo_path.join("cargo_common_metadata");
|
||||||
|
let publish_exceptions =
|
||||||
|
["fail_publish", "fail_publish_true", "pass_publish_empty"].map(|path| cargo_common_metadata_path.join(path));
|
||||||
|
|
||||||
|
for entry in walkdir::WalkDir::new(ui_cargo_path) {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
let path = entry.path();
|
||||||
|
if path.file_name() != Some(OsStr::new("Cargo.toml")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let toml = fs::read_to_string(path).unwrap().parse::<toml::Value>().unwrap();
|
||||||
|
|
||||||
|
let package = toml.as_table().unwrap().get("package").unwrap().as_table().unwrap();
|
||||||
|
|
||||||
|
let name = package.get("name").unwrap().as_str().unwrap().replace('-', "_");
|
||||||
|
assert!(
|
||||||
|
path.parent()
|
||||||
|
.unwrap()
|
||||||
|
.components()
|
||||||
|
.map(|component| component.as_os_str().to_string_lossy().replace('-', "_"))
|
||||||
|
.any(|s| *s == name)
|
||||||
|
|| path.starts_with(&cargo_common_metadata_path),
|
||||||
|
"{:?} has incorrect package name",
|
||||||
|
path
|
||||||
|
);
|
||||||
|
|
||||||
|
let publish = package.get("publish").and_then(toml::Value::as_bool).unwrap_or(true);
|
||||||
|
assert!(
|
||||||
|
!publish || publish_exceptions.contains(&path.parent().unwrap().to_path_buf()),
|
||||||
|
"{:?} lacks `publish = false`",
|
||||||
|
path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Restores an env var on drop
|
/// Restores an env var on drop
|
||||||
#[must_use]
|
#[must_use]
|
||||||
struct VarGuard {
|
struct VarGuard {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cargo_common_metadata"
|
name = "cargo_common_metadata_fail"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
error: package `cargo_common_metadata` is missing `package.description` metadata
|
error: package `cargo_common_metadata_fail` is missing `package.description` metadata
|
||||||
|
|
|
|
||||||
= note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
|
= note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata
|
error: package `cargo_common_metadata_fail` is missing `either package.license or package.license_file` metadata
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `package.repository` metadata
|
error: package `cargo_common_metadata_fail` is missing `package.repository` metadata
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `package.readme` metadata
|
error: package `cargo_common_metadata_fail` is missing `package.readme` metadata
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `package.keywords` metadata
|
error: package `cargo_common_metadata_fail` is missing `package.keywords` metadata
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `package.categories` metadata
|
error: package `cargo_common_metadata_fail` is missing `package.categories` metadata
|
||||||
|
|
||||||
error: aborting due to 6 previous errors
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cargo_common_metadata"
|
name = "cargo_common_metadata_fail_publish"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
publish = ["some-registry-name"]
|
publish = ["some-registry-name"]
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
error: package `cargo_common_metadata` is missing `package.description` metadata
|
error: package `cargo_common_metadata_fail_publish` is missing `package.description` metadata
|
||||||
|
|
|
|
||||||
= note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
|
= note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata
|
error: package `cargo_common_metadata_fail_publish` is missing `either package.license or package.license_file` metadata
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `package.repository` metadata
|
error: package `cargo_common_metadata_fail_publish` is missing `package.repository` metadata
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `package.readme` metadata
|
error: package `cargo_common_metadata_fail_publish` is missing `package.readme` metadata
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `package.keywords` metadata
|
error: package `cargo_common_metadata_fail_publish` is missing `package.keywords` metadata
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `package.categories` metadata
|
error: package `cargo_common_metadata_fail_publish` is missing `package.categories` metadata
|
||||||
|
|
||||||
error: aborting due to 6 previous errors
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cargo_common_metadata"
|
name = "cargo_common_metadata_fail_publish_true"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
publish = true
|
publish = true
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
error: package `cargo_common_metadata` is missing `package.description` metadata
|
error: package `cargo_common_metadata_fail_publish_true` is missing `package.description` metadata
|
||||||
|
|
|
|
||||||
= note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
|
= note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata
|
error: package `cargo_common_metadata_fail_publish_true` is missing `either package.license or package.license_file` metadata
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `package.repository` metadata
|
error: package `cargo_common_metadata_fail_publish_true` is missing `package.repository` metadata
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `package.readme` metadata
|
error: package `cargo_common_metadata_fail_publish_true` is missing `package.readme` metadata
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `package.keywords` metadata
|
error: package `cargo_common_metadata_fail_publish_true` is missing `package.keywords` metadata
|
||||||
|
|
||||||
error: package `cargo_common_metadata` is missing `package.categories` metadata
|
error: package `cargo_common_metadata_fail_publish_true` is missing `package.categories` metadata
|
||||||
|
|
||||||
error: aborting due to 6 previous errors
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cargo_common_metadata"
|
name = "cargo_common_metadata_pass"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
publish = false
|
publish = false
|
||||||
description = "A test package for the cargo_common_metadata lint"
|
description = "A test package for the cargo_common_metadata lint"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cargo_common_metadata"
|
name = "cargo_common_metadata_pass_publish_empty"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
publish = []
|
publish = []
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cargo_common_metadata"
|
name = "cargo_common_metadata_pass_publish_false"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
name = "fail-both-diff"
|
name = "fail-both-diff"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
rust-version = "1.56"
|
rust-version = "1.56"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
name = "fail-both-same"
|
name = "fail-both-same"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
rust-version = "1.57.0"
|
rust-version = "1.57.0"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
name = "fail-cargo"
|
name = "fail-cargo"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
rust-version = "1.56.1"
|
rust-version = "1.56.1"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fail-clippy"
|
name = "fail-clippy"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
name = "fail-file-attr"
|
name = "fail-file-attr"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
rust-version = "1.13"
|
rust-version = "1.13"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fail-both-same"
|
name = "pass-both-same"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
rust-version = "1.13.0"
|
rust-version = "1.13.0"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fail-cargo"
|
name = "pass-cargo"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
rust-version = "1.13.0"
|
rust-version = "1.13.0"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fail-clippy"
|
name = "pass-clippy"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fail-file-attr"
|
name = "pass-file-attr"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
rust-version = "1.59"
|
rust-version = "1.59"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
name = "warn-both-diff"
|
name = "warn-both-diff"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
rust-version = "1.56.0"
|
rust-version = "1.56.0"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fail"
|
name = "fail-mod"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fail"
|
name = "fail-no-mod"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fail"
|
name = "pass-mod"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pass"
|
name = "pass-no-mod"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
name = "no_warn"
|
name = "no_warn"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
name = "warn"
|
name = "warn"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// ignore-windows
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cargo_common_metadata"
|
name = "multiple_crate_versions"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ LL | #![deny(clippy::internal)]
|
|||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
= note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
|
= note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
|
||||||
= help: please use a valid sematic version, see `doc/adding_lints.md`
|
= help: please use a valid sematic version, see `doc/adding_lints.md`
|
||||||
= note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: this item has an invalid `clippy::version` attribute
|
error: this item has an invalid `clippy::version` attribute
|
||||||
--> $DIR/check_clippy_version_attribute.rs:48:1
|
--> $DIR/check_clippy_version_attribute.rs:48:1
|
||||||
@ -32,7 +32,7 @@ LL | | }
|
|||||||
| |_^
|
| |_^
|
||||||
|
|
|
|
||||||
= help: please use a valid sematic version, see `doc/adding_lints.md`
|
= help: please use a valid sematic version, see `doc/adding_lints.md`
|
||||||
= note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: this lint is missing the `clippy::version` attribute or version value
|
error: this lint is missing the `clippy::version` attribute or version value
|
||||||
--> $DIR/check_clippy_version_attribute.rs:59:1
|
--> $DIR/check_clippy_version_attribute.rs:59:1
|
||||||
@ -48,7 +48,7 @@ LL | | }
|
|||||||
|
|
|
|
||||||
= note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
|
= note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
|
||||||
= help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
|
= help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
|
||||||
= note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: this lint is missing the `clippy::version` attribute or version value
|
error: this lint is missing the `clippy::version` attribute or version value
|
||||||
--> $DIR/check_clippy_version_attribute.rs:67:1
|
--> $DIR/check_clippy_version_attribute.rs:67:1
|
||||||
@ -62,7 +62,7 @@ LL | | }
|
|||||||
| |_^
|
| |_^
|
||||||
|
|
|
|
||||||
= help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
|
= help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
|
||||||
= note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ note: the lint level is defined here
|
|||||||
LL | #![deny(clippy::internal)]
|
LL | #![deny(clippy::internal)]
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
= note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]`
|
= note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]`
|
||||||
= note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ LL | | }
|
|||||||
LL | | }
|
LL | | }
|
||||||
| |_____^
|
| |_____^
|
||||||
|
|
|
|
||||||
= note: this error originates in the macro `__if_chain` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `__if_chain` which comes from the expansion of the macro `if_chain` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: `let` expression should be above the `if_chain!`
|
error: `let` expression should be above the `if_chain!`
|
||||||
--> $DIR/if_chain_style.rs:40:9
|
--> $DIR/if_chain_style.rs:40:9
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
#![warn(clippy::arithmetic)]
|
||||||
|
|
||||||
|
use core::ops::Add;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Point {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Point {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = Point { x: 1, y: 0 } + Point { x: 2, y: 3 };
|
||||||
|
|
||||||
|
let point: Point = Point { x: 1, y: 0 };
|
||||||
|
let _ = point + point;
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
arithmetic-allowed = ["Point"]
|
@ -3,6 +3,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie
|
|||||||
allow-expect-in-tests
|
allow-expect-in-tests
|
||||||
allow-unwrap-in-tests
|
allow-unwrap-in-tests
|
||||||
allowed-scripts
|
allowed-scripts
|
||||||
|
arithmetic-allowed
|
||||||
array-size-threshold
|
array-size-threshold
|
||||||
avoid-breaking-exported-api
|
avoid-breaking-exported-api
|
||||||
await-holding-invalid-types
|
await-holding-invalid-types
|
||||||
|
27
src/tools/clippy/tests/ui/arithmetic.fixed
Normal file
27
src/tools/clippy/tests/ui/arithmetic.fixed
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![allow(clippy::unnecessary_owned_empty_strings)]
|
||||||
|
#![feature(saturating_int_impl)]
|
||||||
|
#![warn(clippy::arithmetic)]
|
||||||
|
|
||||||
|
use core::num::{Saturating, Wrapping};
|
||||||
|
|
||||||
|
pub fn hard_coded_allowed() {
|
||||||
|
let _ = Saturating(0u32) + Saturating(0u32);
|
||||||
|
let _ = String::new() + "";
|
||||||
|
let _ = Wrapping(0u32) + Wrapping(0u32);
|
||||||
|
|
||||||
|
let saturating: Saturating<u32> = Saturating(0u32);
|
||||||
|
let string: String = String::new();
|
||||||
|
let wrapping: Wrapping<u32> = Wrapping(0u32);
|
||||||
|
|
||||||
|
let inferred_saturating = saturating + saturating;
|
||||||
|
let inferred_string = string + "";
|
||||||
|
let inferred_wrapping = wrapping + wrapping;
|
||||||
|
|
||||||
|
let _ = inferred_saturating + inferred_saturating;
|
||||||
|
let _ = inferred_string + "";
|
||||||
|
let _ = inferred_wrapping + inferred_wrapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
27
src/tools/clippy/tests/ui/arithmetic.rs
Normal file
27
src/tools/clippy/tests/ui/arithmetic.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![allow(clippy::unnecessary_owned_empty_strings)]
|
||||||
|
#![feature(saturating_int_impl)]
|
||||||
|
#![warn(clippy::arithmetic)]
|
||||||
|
|
||||||
|
use core::num::{Saturating, Wrapping};
|
||||||
|
|
||||||
|
pub fn hard_coded_allowed() {
|
||||||
|
let _ = Saturating(0u32) + Saturating(0u32);
|
||||||
|
let _ = String::new() + "";
|
||||||
|
let _ = Wrapping(0u32) + Wrapping(0u32);
|
||||||
|
|
||||||
|
let saturating: Saturating<u32> = Saturating(0u32);
|
||||||
|
let string: String = String::new();
|
||||||
|
let wrapping: Wrapping<u32> = Wrapping(0u32);
|
||||||
|
|
||||||
|
let inferred_saturating = saturating + saturating;
|
||||||
|
let inferred_string = string + "";
|
||||||
|
let inferred_wrapping = wrapping + wrapping;
|
||||||
|
|
||||||
|
let _ = inferred_saturating + inferred_saturating;
|
||||||
|
let _ = inferred_string + "";
|
||||||
|
let _ = inferred_wrapping + inferred_wrapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
69
src/tools/clippy/tests/ui/assertions_on_result_states.fixed
Normal file
69
src/tools/clippy/tests/ui/assertions_on_result_states.fixed
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// run-rustfix
|
||||||
|
#![warn(clippy::assertions_on_result_states)]
|
||||||
|
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct DebugFoo;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
struct CopyFoo;
|
||||||
|
|
||||||
|
macro_rules! get_ok_macro {
|
||||||
|
() => {
|
||||||
|
Ok::<_, DebugFoo>(Foo)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// test ok
|
||||||
|
let r: Result<Foo, DebugFoo> = Ok(Foo);
|
||||||
|
debug_assert!(r.is_ok());
|
||||||
|
r.unwrap();
|
||||||
|
|
||||||
|
// test ok with non-debug error type
|
||||||
|
let r: Result<Foo, Foo> = Ok(Foo);
|
||||||
|
assert!(r.is_ok());
|
||||||
|
|
||||||
|
// test temporary ok
|
||||||
|
fn get_ok() -> Result<Foo, DebugFoo> {
|
||||||
|
Ok(Foo)
|
||||||
|
}
|
||||||
|
get_ok().unwrap();
|
||||||
|
|
||||||
|
// test macro ok
|
||||||
|
get_ok_macro!().unwrap();
|
||||||
|
|
||||||
|
// test ok that shouldn't be moved
|
||||||
|
let r: Result<CopyFoo, DebugFoo> = Ok(CopyFoo);
|
||||||
|
fn test_ref_unmoveable_ok(r: &Result<CopyFoo, DebugFoo>) {
|
||||||
|
assert!(r.is_ok());
|
||||||
|
}
|
||||||
|
test_ref_unmoveable_ok(&r);
|
||||||
|
assert!(r.is_ok());
|
||||||
|
r.unwrap();
|
||||||
|
|
||||||
|
// test ok that is copied
|
||||||
|
let r: Result<CopyFoo, CopyFoo> = Ok(CopyFoo);
|
||||||
|
r.unwrap();
|
||||||
|
r.unwrap();
|
||||||
|
|
||||||
|
// test reference to ok
|
||||||
|
let r: Result<CopyFoo, CopyFoo> = Ok(CopyFoo);
|
||||||
|
fn test_ref_copy_ok(r: &Result<CopyFoo, CopyFoo>) {
|
||||||
|
r.unwrap();
|
||||||
|
}
|
||||||
|
test_ref_copy_ok(&r);
|
||||||
|
r.unwrap();
|
||||||
|
|
||||||
|
// test err
|
||||||
|
let r: Result<DebugFoo, Foo> = Err(Foo);
|
||||||
|
debug_assert!(r.is_err());
|
||||||
|
r.unwrap_err();
|
||||||
|
|
||||||
|
// test err with non-debug value type
|
||||||
|
let r: Result<Foo, Foo> = Err(Foo);
|
||||||
|
assert!(r.is_err());
|
||||||
|
}
|
69
src/tools/clippy/tests/ui/assertions_on_result_states.rs
Normal file
69
src/tools/clippy/tests/ui/assertions_on_result_states.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// run-rustfix
|
||||||
|
#![warn(clippy::assertions_on_result_states)]
|
||||||
|
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct DebugFoo;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
struct CopyFoo;
|
||||||
|
|
||||||
|
macro_rules! get_ok_macro {
|
||||||
|
() => {
|
||||||
|
Ok::<_, DebugFoo>(Foo)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// test ok
|
||||||
|
let r: Result<Foo, DebugFoo> = Ok(Foo);
|
||||||
|
debug_assert!(r.is_ok());
|
||||||
|
assert!(r.is_ok());
|
||||||
|
|
||||||
|
// test ok with non-debug error type
|
||||||
|
let r: Result<Foo, Foo> = Ok(Foo);
|
||||||
|
assert!(r.is_ok());
|
||||||
|
|
||||||
|
// test temporary ok
|
||||||
|
fn get_ok() -> Result<Foo, DebugFoo> {
|
||||||
|
Ok(Foo)
|
||||||
|
}
|
||||||
|
assert!(get_ok().is_ok());
|
||||||
|
|
||||||
|
// test macro ok
|
||||||
|
assert!(get_ok_macro!().is_ok());
|
||||||
|
|
||||||
|
// test ok that shouldn't be moved
|
||||||
|
let r: Result<CopyFoo, DebugFoo> = Ok(CopyFoo);
|
||||||
|
fn test_ref_unmoveable_ok(r: &Result<CopyFoo, DebugFoo>) {
|
||||||
|
assert!(r.is_ok());
|
||||||
|
}
|
||||||
|
test_ref_unmoveable_ok(&r);
|
||||||
|
assert!(r.is_ok());
|
||||||
|
r.unwrap();
|
||||||
|
|
||||||
|
// test ok that is copied
|
||||||
|
let r: Result<CopyFoo, CopyFoo> = Ok(CopyFoo);
|
||||||
|
assert!(r.is_ok());
|
||||||
|
r.unwrap();
|
||||||
|
|
||||||
|
// test reference to ok
|
||||||
|
let r: Result<CopyFoo, CopyFoo> = Ok(CopyFoo);
|
||||||
|
fn test_ref_copy_ok(r: &Result<CopyFoo, CopyFoo>) {
|
||||||
|
assert!(r.is_ok());
|
||||||
|
}
|
||||||
|
test_ref_copy_ok(&r);
|
||||||
|
r.unwrap();
|
||||||
|
|
||||||
|
// test err
|
||||||
|
let r: Result<DebugFoo, Foo> = Err(Foo);
|
||||||
|
debug_assert!(r.is_err());
|
||||||
|
assert!(r.is_err());
|
||||||
|
|
||||||
|
// test err with non-debug value type
|
||||||
|
let r: Result<Foo, Foo> = Err(Foo);
|
||||||
|
assert!(r.is_err());
|
||||||
|
}
|
40
src/tools/clippy/tests/ui/assertions_on_result_states.stderr
Normal file
40
src/tools/clippy/tests/ui/assertions_on_result_states.stderr
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
error: called `assert!` with `Result::is_ok`
|
||||||
|
--> $DIR/assertions_on_result_states.rs:24:5
|
||||||
|
|
|
||||||
|
LL | assert!(r.is_ok());
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::assertions-on-result-states` implied by `-D warnings`
|
||||||
|
|
||||||
|
error: called `assert!` with `Result::is_ok`
|
||||||
|
--> $DIR/assertions_on_result_states.rs:34:5
|
||||||
|
|
|
||||||
|
LL | assert!(get_ok().is_ok());
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get_ok().unwrap()`
|
||||||
|
|
||||||
|
error: called `assert!` with `Result::is_ok`
|
||||||
|
--> $DIR/assertions_on_result_states.rs:37:5
|
||||||
|
|
|
||||||
|
LL | assert!(get_ok_macro!().is_ok());
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get_ok_macro!().unwrap()`
|
||||||
|
|
||||||
|
error: called `assert!` with `Result::is_ok`
|
||||||
|
--> $DIR/assertions_on_result_states.rs:50:5
|
||||||
|
|
|
||||||
|
LL | assert!(r.is_ok());
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()`
|
||||||
|
|
||||||
|
error: called `assert!` with `Result::is_ok`
|
||||||
|
--> $DIR/assertions_on_result_states.rs:56:9
|
||||||
|
|
|
||||||
|
LL | assert!(r.is_ok());
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()`
|
||||||
|
|
||||||
|
error: called `assert!` with `Result::is_err`
|
||||||
|
--> $DIR/assertions_on_result_states.rs:64:5
|
||||||
|
|
|
||||||
|
LL | assert!(r.is_err());
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap_err()`
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
@ -1,5 +1,7 @@
|
|||||||
// run-rustfix
|
// run-rustfix
|
||||||
|
|
||||||
|
use core::num::Wrapping;
|
||||||
|
|
||||||
#[allow(dead_code, unused_assignments)]
|
#[allow(dead_code, unused_assignments)]
|
||||||
#[warn(clippy::assign_op_pattern)]
|
#[warn(clippy::assign_op_pattern)]
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -18,4 +20,13 @@ fn main() {
|
|||||||
a = 6 << a;
|
a = 6 << a;
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s += "bla";
|
s += "bla";
|
||||||
|
|
||||||
|
// Issue #9180
|
||||||
|
let mut a = Wrapping(0u32);
|
||||||
|
a += Wrapping(1u32);
|
||||||
|
let mut v = vec![0u32, 1u32];
|
||||||
|
v[0] += v[1];
|
||||||
|
let mut v = vec![Wrapping(0u32), Wrapping(1u32)];
|
||||||
|
v[0] = v[0] + v[1];
|
||||||
|
let _ = || v[0] = v[0] + v[1];
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
// run-rustfix
|
// run-rustfix
|
||||||
|
|
||||||
|
use core::num::Wrapping;
|
||||||
|
|
||||||
#[allow(dead_code, unused_assignments)]
|
#[allow(dead_code, unused_assignments)]
|
||||||
#[warn(clippy::assign_op_pattern)]
|
#[warn(clippy::assign_op_pattern)]
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -18,4 +20,13 @@ fn main() {
|
|||||||
a = 6 << a;
|
a = 6 << a;
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s = s + "bla";
|
s = s + "bla";
|
||||||
|
|
||||||
|
// Issue #9180
|
||||||
|
let mut a = Wrapping(0u32);
|
||||||
|
a = a + Wrapping(1u32);
|
||||||
|
let mut v = vec![0u32, 1u32];
|
||||||
|
v[0] = v[0] + v[1];
|
||||||
|
let mut v = vec![Wrapping(0u32), Wrapping(1u32)];
|
||||||
|
v[0] = v[0] + v[1];
|
||||||
|
let _ = || v[0] = v[0] + v[1];
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error: manual implementation of an assign operation
|
error: manual implementation of an assign operation
|
||||||
--> $DIR/assign_ops.rs:7:5
|
--> $DIR/assign_ops.rs:9:5
|
||||||
|
|
|
|
||||||
LL | a = a + 1;
|
LL | a = a + 1;
|
||||||
| ^^^^^^^^^ help: replace it with: `a += 1`
|
| ^^^^^^^^^ help: replace it with: `a += 1`
|
||||||
@ -7,52 +7,64 @@ LL | a = a + 1;
|
|||||||
= note: `-D clippy::assign-op-pattern` implied by `-D warnings`
|
= note: `-D clippy::assign-op-pattern` implied by `-D warnings`
|
||||||
|
|
||||||
error: manual implementation of an assign operation
|
error: manual implementation of an assign operation
|
||||||
--> $DIR/assign_ops.rs:8:5
|
--> $DIR/assign_ops.rs:10:5
|
||||||
|
|
|
|
||||||
LL | a = 1 + a;
|
LL | a = 1 + a;
|
||||||
| ^^^^^^^^^ help: replace it with: `a += 1`
|
| ^^^^^^^^^ help: replace it with: `a += 1`
|
||||||
|
|
||||||
error: manual implementation of an assign operation
|
error: manual implementation of an assign operation
|
||||||
--> $DIR/assign_ops.rs:9:5
|
--> $DIR/assign_ops.rs:11:5
|
||||||
|
|
|
|
||||||
LL | a = a - 1;
|
LL | a = a - 1;
|
||||||
| ^^^^^^^^^ help: replace it with: `a -= 1`
|
| ^^^^^^^^^ help: replace it with: `a -= 1`
|
||||||
|
|
||||||
error: manual implementation of an assign operation
|
error: manual implementation of an assign operation
|
||||||
--> $DIR/assign_ops.rs:10:5
|
--> $DIR/assign_ops.rs:12:5
|
||||||
|
|
|
|
||||||
LL | a = a * 99;
|
LL | a = a * 99;
|
||||||
| ^^^^^^^^^^ help: replace it with: `a *= 99`
|
| ^^^^^^^^^^ help: replace it with: `a *= 99`
|
||||||
|
|
||||||
error: manual implementation of an assign operation
|
error: manual implementation of an assign operation
|
||||||
--> $DIR/assign_ops.rs:11:5
|
--> $DIR/assign_ops.rs:13:5
|
||||||
|
|
|
|
||||||
LL | a = 42 * a;
|
LL | a = 42 * a;
|
||||||
| ^^^^^^^^^^ help: replace it with: `a *= 42`
|
| ^^^^^^^^^^ help: replace it with: `a *= 42`
|
||||||
|
|
||||||
error: manual implementation of an assign operation
|
error: manual implementation of an assign operation
|
||||||
--> $DIR/assign_ops.rs:12:5
|
--> $DIR/assign_ops.rs:14:5
|
||||||
|
|
|
|
||||||
LL | a = a / 2;
|
LL | a = a / 2;
|
||||||
| ^^^^^^^^^ help: replace it with: `a /= 2`
|
| ^^^^^^^^^ help: replace it with: `a /= 2`
|
||||||
|
|
||||||
error: manual implementation of an assign operation
|
error: manual implementation of an assign operation
|
||||||
--> $DIR/assign_ops.rs:13:5
|
--> $DIR/assign_ops.rs:15:5
|
||||||
|
|
|
|
||||||
LL | a = a % 5;
|
LL | a = a % 5;
|
||||||
| ^^^^^^^^^ help: replace it with: `a %= 5`
|
| ^^^^^^^^^ help: replace it with: `a %= 5`
|
||||||
|
|
||||||
error: manual implementation of an assign operation
|
error: manual implementation of an assign operation
|
||||||
--> $DIR/assign_ops.rs:14:5
|
--> $DIR/assign_ops.rs:16:5
|
||||||
|
|
|
|
||||||
LL | a = a & 1;
|
LL | a = a & 1;
|
||||||
| ^^^^^^^^^ help: replace it with: `a &= 1`
|
| ^^^^^^^^^ help: replace it with: `a &= 1`
|
||||||
|
|
||||||
error: manual implementation of an assign operation
|
error: manual implementation of an assign operation
|
||||||
--> $DIR/assign_ops.rs:20:5
|
--> $DIR/assign_ops.rs:22:5
|
||||||
|
|
|
|
||||||
LL | s = s + "bla";
|
LL | s = s + "bla";
|
||||||
| ^^^^^^^^^^^^^ help: replace it with: `s += "bla"`
|
| ^^^^^^^^^^^^^ help: replace it with: `s += "bla"`
|
||||||
|
|
||||||
error: aborting due to 9 previous errors
|
error: manual implementation of an assign operation
|
||||||
|
--> $DIR/assign_ops.rs:26:5
|
||||||
|
|
|
||||||
|
LL | a = a + Wrapping(1u32);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)`
|
||||||
|
|
||||||
|
error: manual implementation of an assign operation
|
||||||
|
--> $DIR/assign_ops.rs:28:5
|
||||||
|
|
|
||||||
|
LL | v[0] = v[0] + v[1];
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]`
|
||||||
|
|
||||||
|
error: aborting due to 11 previous errors
|
||||||
|
|
||||||
|
12
src/tools/clippy/tests/ui/crashes/ice-9238.rs
Normal file
12
src/tools/clippy/tests/ui/crashes/ice-9238.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![feature(generic_const_exprs)]
|
||||||
|
#![warn(clippy::branches_sharing_code)]
|
||||||
|
|
||||||
|
const fn f() -> usize {
|
||||||
|
2
|
||||||
|
}
|
||||||
|
const C: [f64; f()] = [0f64; f()];
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = if true { C[0] } else { C[1] };
|
||||||
|
}
|
8
src/tools/clippy/tests/ui/crashes/ice-9242.rs
Normal file
8
src/tools/clippy/tests/ui/crashes/ice-9242.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
enum E {
|
||||||
|
X(),
|
||||||
|
Y,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = if let E::X() = E::X() { 1 } else { 2 };
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
// ignore-macos
|
// ignore-macos
|
||||||
// ignore-windows
|
|
||||||
|
|
||||||
#![feature(rustc_attrs)]
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error: recursing into entrypoint `a`
|
error: recursing into entrypoint `a`
|
||||||
--> $DIR/entrypoint_recursion.rs:11:5
|
--> $DIR/entrypoint_recursion.rs:10:5
|
||||||
|
|
|
|
||||||
LL | a();
|
LL | a();
|
||||||
| ^
|
| ^
|
||||||
|
@ -31,9 +31,25 @@ const NO_ANN: &dyn Display = &70;
|
|||||||
static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
|
static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
|
||||||
//^ there should be no lints on this line
|
//^ there should be no lints on this line
|
||||||
|
|
||||||
// issue #8493
|
mod issue_8493 {
|
||||||
thread_local! {
|
use std::cell::Cell;
|
||||||
static THREAD_LOCAL: Cell<i32> = const { Cell::new(0) };
|
|
||||||
|
thread_local! {
|
||||||
|
static _BAR: Cell<i32> = const { Cell::new(0) };
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! issue_8493 {
|
||||||
|
() => {
|
||||||
|
const _BAZ: Cell<usize> = Cell::new(0); //~ ERROR interior mutable
|
||||||
|
static _FOOBAR: () = {
|
||||||
|
thread_local! {
|
||||||
|
static _VAR: Cell<i32> = const { Cell::new(0) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_8493!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -35,5 +35,16 @@ LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
|
|||||||
|
|
|
|
||||||
= note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: a `const` item should never be interior mutable
|
||||||
|
--> $DIR/others.rs:43:13
|
||||||
|
|
|
||||||
|
LL | const _BAZ: Cell<usize> = Cell::new(0); //~ ERROR interior mutable
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
...
|
||||||
|
LL | issue_8493!();
|
||||||
|
| ------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `issue_8493` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: aborting due to 5 previous errors
|
||||||
|
|
||||||
|
@ -84,4 +84,10 @@ fn main() {
|
|||||||
let _ = x.to_string();
|
let _ = x.to_string();
|
||||||
let _ = format!("{x:?}"); // Don't lint on debug
|
let _ = format!("{x:?}"); // Don't lint on debug
|
||||||
let _ = x.to_string();
|
let _ = x.to_string();
|
||||||
|
|
||||||
|
// Issue #9234
|
||||||
|
let abc = "abc";
|
||||||
|
let _ = abc.to_string();
|
||||||
|
let xx = "xx";
|
||||||
|
let _ = xx.to_string();
|
||||||
}
|
}
|
||||||
|
@ -86,4 +86,10 @@ fn main() {
|
|||||||
let _ = format!("{x}");
|
let _ = format!("{x}");
|
||||||
let _ = format!("{x:?}"); // Don't lint on debug
|
let _ = format!("{x:?}"); // Don't lint on debug
|
||||||
let _ = format!("{y}", y = x);
|
let _ = format!("{y}", y = x);
|
||||||
|
|
||||||
|
// Issue #9234
|
||||||
|
let abc = "abc";
|
||||||
|
let _ = format!("{abc}");
|
||||||
|
let xx = "xx";
|
||||||
|
let _ = format!("{xx}");
|
||||||
}
|
}
|
||||||
|
@ -111,5 +111,17 @@ error: useless use of `format!`
|
|||||||
LL | let _ = format!("{y}", y = x);
|
LL | let _ = format!("{y}", y = x);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
|
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
|
||||||
|
|
||||||
error: aborting due to 17 previous errors
|
error: useless use of `format!`
|
||||||
|
--> $DIR/format.rs:92:13
|
||||||
|
|
|
||||||
|
LL | let _ = format!("{abc}");
|
||||||
|
| ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
|
||||||
|
|
||||||
|
error: useless use of `format!`
|
||||||
|
--> $DIR/format.rs:94:13
|
||||||
|
|
|
||||||
|
LL | let _ = format!("{xx}");
|
||||||
|
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`
|
||||||
|
|
||||||
|
error: aborting due to 19 previous errors
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![warn(clippy::match_same_arms)]
|
#![warn(clippy::match_same_arms)]
|
||||||
#![allow(clippy::blacklisted_name)]
|
#![allow(clippy::blacklisted_name, clippy::diverging_sub_expression)]
|
||||||
|
|
||||||
fn bar<T>(_: T) {}
|
fn bar<T>(_: T) {}
|
||||||
fn foo() -> bool {
|
fn foo() -> bool {
|
||||||
@ -227,4 +227,12 @@ fn main() {
|
|||||||
Some(Bar { y: 0, x: 5, .. }) => 1,
|
Some(Bar { y: 0, x: 5, .. }) => 1,
|
||||||
_ => 200,
|
_ => 200,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let _ = match 0 {
|
||||||
|
0 => todo!(),
|
||||||
|
1 => todo!(),
|
||||||
|
2 => core::convert::identity::<u32>(todo!()),
|
||||||
|
3 => core::convert::identity::<u32>(todo!()),
|
||||||
|
_ => 5,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -57,4 +57,8 @@ fn main() {
|
|||||||
B: Copy,
|
B: Copy,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the types are complicated, do not lint
|
||||||
|
impl<K, V, B> Foo<(K, V), B> {}
|
||||||
|
impl<K, V, A> Foo<(K, V), A> {}
|
||||||
}
|
}
|
||||||
|
@ -158,3 +158,28 @@ fn check_expect_suppression() {
|
|||||||
#[expect(clippy::needless_borrow)]
|
#[expect(clippy::needless_borrow)]
|
||||||
let _ = x(&&a);
|
let _ = x(&&a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
mod issue9160 {
|
||||||
|
pub struct S<F> {
|
||||||
|
f: F,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, F> S<F>
|
||||||
|
where
|
||||||
|
F: Fn() -> T,
|
||||||
|
{
|
||||||
|
fn calls_field(&self) -> T {
|
||||||
|
(self.f)()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, F> S<F>
|
||||||
|
where
|
||||||
|
F: FnMut() -> T,
|
||||||
|
{
|
||||||
|
fn calls_mut_field(&mut self) -> T {
|
||||||
|
(self.f)()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -158,3 +158,28 @@ fn check_expect_suppression() {
|
|||||||
#[expect(clippy::needless_borrow)]
|
#[expect(clippy::needless_borrow)]
|
||||||
let _ = x(&&a);
|
let _ = x(&&a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
mod issue9160 {
|
||||||
|
pub struct S<F> {
|
||||||
|
f: F,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, F> S<F>
|
||||||
|
where
|
||||||
|
F: Fn() -> T,
|
||||||
|
{
|
||||||
|
fn calls_field(&self) -> T {
|
||||||
|
(&self.f)()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, F> S<F>
|
||||||
|
where
|
||||||
|
F: FnMut() -> T,
|
||||||
|
{
|
||||||
|
fn calls_mut_field(&mut self) -> T {
|
||||||
|
(&mut self.f)()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -120,5 +120,17 @@ error: this expression creates a reference which is immediately dereferenced by
|
|||||||
LL | (&&5).foo();
|
LL | (&&5).foo();
|
||||||
| ^^^^^ help: change this to: `(&5)`
|
| ^^^^^ help: change this to: `(&5)`
|
||||||
|
|
||||||
error: aborting due to 20 previous errors
|
error: this expression borrows a value the compiler would automatically borrow
|
||||||
|
--> $DIR/needless_borrow.rs:173:13
|
||||||
|
|
|
||||||
|
LL | (&self.f)()
|
||||||
|
| ^^^^^^^^^ help: change this to: `(self.f)`
|
||||||
|
|
||||||
|
error: this expression borrows a value the compiler would automatically borrow
|
||||||
|
--> $DIR/needless_borrow.rs:182:13
|
||||||
|
|
|
||||||
|
LL | (&mut self.f)()
|
||||||
|
| ^^^^^^^^^^^^^ help: change this to: `(self.f)`
|
||||||
|
|
||||||
|
error: aborting due to 22 previous errors
|
||||||
|
|
||||||
|
7
src/tools/clippy/tests/ui/obfuscated_if_else.fixed
Normal file
7
src/tools/clippy/tests/ui/obfuscated_if_else.fixed
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![warn(clippy::obfuscated_if_else)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if true { "a" } else { "b" };
|
||||||
|
}
|
7
src/tools/clippy/tests/ui/obfuscated_if_else.rs
Normal file
7
src/tools/clippy/tests/ui/obfuscated_if_else.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![warn(clippy::obfuscated_if_else)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
true.then_some("a").unwrap_or("b");
|
||||||
|
}
|
10
src/tools/clippy/tests/ui/obfuscated_if_else.stderr
Normal file
10
src/tools/clippy/tests/ui/obfuscated_if_else.stderr
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
error: use of `.then_some(..).unwrap_or(..)` can be written more clearly with `if .. else ..`
|
||||||
|
--> $DIR/obfuscated_if_else.rs:6:5
|
||||||
|
|
|
||||||
|
LL | true.then_some("a").unwrap_or("b");
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { "a" } else { "b" }`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::obfuscated-if-else` implied by `-D warnings`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
@ -9,6 +9,8 @@ fn std_instead_of_core() {
|
|||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
// Absolute path
|
// Absolute path
|
||||||
use ::std::hash::Hash;
|
use ::std::hash::Hash;
|
||||||
|
// Don't lint on `env` macro
|
||||||
|
use std::env;
|
||||||
|
|
||||||
// Multiple imports
|
// Multiple imports
|
||||||
use std::fmt::{Debug, Result};
|
use std::fmt::{Debug, Result};
|
||||||
@ -20,10 +22,14 @@ fn std_instead_of_core() {
|
|||||||
// Types
|
// Types
|
||||||
let cell = std::cell::Cell::new(8u32);
|
let cell = std::cell::Cell::new(8u32);
|
||||||
let cell_absolute = ::std::cell::Cell::new(8u32);
|
let cell_absolute = ::std::cell::Cell::new(8u32);
|
||||||
|
|
||||||
|
let _ = std::env!("PATH");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[warn(clippy::std_instead_of_alloc)]
|
#[warn(clippy::std_instead_of_alloc)]
|
||||||
fn std_instead_of_alloc() {
|
fn std_instead_of_alloc() {
|
||||||
|
// Only lint once.
|
||||||
|
use std::vec;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user