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:
parent
c31637e082
commit
67c405cc1d
@ -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_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
|
||||
[`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_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_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_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
|
||||
@ -3793,6 +3795,7 @@ Released 2018-09-13
|
||||
[`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
|
||||
[`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
|
||||
[`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
|
||||
|
@ -32,6 +32,7 @@ compiletest_rs = { version = "0.8", features = ["tmp"] }
|
||||
tester = "0.9"
|
||||
regex = "1.5"
|
||||
toml = "0.5"
|
||||
walkdir = "2.3"
|
||||
# This is used by the `collect-metadata` alias.
|
||||
filetime = "0.2"
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# GitHub Actions
|
||||
|
||||
On the GitHub hosted runners, Clippy from the latest stable Rust version comes
|
||||
pre-installed. So all you have to do is to run `cargo clippy`.
|
||||
GitHub hosted runners using the latest stable version of Rust have Clippy pre-installed.
|
||||
It is as simple as running `cargo clippy` to run lints against the codebase.
|
||||
|
||||
```yml
|
||||
on: push
|
||||
@ -15,7 +15,7 @@ jobs:
|
||||
clippy_check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run Clippy
|
||||
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)
|
||||
- [Setup](#setup)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Defining Our Lint](#defining-our-lint)
|
||||
- [Standalone](#standalone)
|
||||
- [Specific Type](#specific-type)
|
||||
- [Tests Location](#tests-location)
|
||||
- [Testing](#testing)
|
||||
- [Cargo lints](#cargo-lints)
|
||||
- [Rustfix tests](#rustfix-tests)
|
||||
@ -36,17 +40,38 @@ See the [Basics](basics.md#get-the-code) documentation.
|
||||
## Getting Started
|
||||
|
||||
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
|
||||
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,
|
||||
take a look at our [lint naming guidelines][lint_naming]. To get started on this
|
||||
lint you can run `cargo dev new_lint --name=foo_functions --pass=early
|
||||
--category=pedantic` (category will default to nursery if not provided). This
|
||||
command will create two files: `tests/ui/foo_functions.rs` and
|
||||
`clippy_lints/src/foo_functions.rs`, as well as [registering the
|
||||
lint](#lint-registration). For cargo lints, two project hierarchies (fail/pass)
|
||||
will be created by default under `tests/ui-cargo`.
|
||||
case), and we don't need type information, so it will have an early pass type
|
||||
(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].
|
||||
|
||||
## Defining Our Lint
|
||||
To get started, there are two ways to define our lint.
|
||||
|
||||
### Standalone
|
||||
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!
|
||||
|
||||
|
@ -25,7 +25,7 @@ instructions for other options.
|
||||
## Make changes
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -13,7 +13,7 @@ pub enum CliError {
|
||||
IoError(io::Error),
|
||||
RustfmtNotInstalled,
|
||||
WalkDirError(walkdir::Error),
|
||||
RaSetupActive,
|
||||
IntellijSetupActive,
|
||||
}
|
||||
|
||||
impl From<io::Error> for CliError {
|
||||
@ -48,7 +48,7 @@ pub fn run(check: bool, verbose: bool) {
|
||||
.expect("Failed to read clippy Cargo.toml")
|
||||
.contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
|
||||
{
|
||||
return Err(CliError::RaSetupActive);
|
||||
return Err(CliError::IntellijSetupActive);
|
||||
}
|
||||
|
||||
rustfmt_test(context)?;
|
||||
@ -93,11 +93,11 @@ pub fn run(check: bool, verbose: bool) {
|
||||
CliError::WalkDirError(err) => {
|
||||
eprintln!("error: {}", err);
|
||||
},
|
||||
CliError::RaSetupActive => {
|
||||
CliError::IntellijSetupActive => {
|
||||
eprintln!(
|
||||
"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!
|
||||
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(
|
||||
matches.get_one::<String>("pass"),
|
||||
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"),
|
||||
) {
|
||||
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")
|
||||
.takes_value(true)
|
||||
.value_parser([PossibleValue::new("early"), PossibleValue::new("late")])
|
||||
.required(true),
|
||||
.conflicts_with("type")
|
||||
.required_unless_present("type"),
|
||||
Arg::new("name")
|
||||
.short('n')
|
||||
.long("name")
|
||||
@ -183,6 +185,11 @@ fn get_clap_config() -> ArgMatches {
|
||||
PossibleValue::new("internal_warn"),
|
||||
])
|
||||
.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"),
|
||||
]),
|
||||
Command::new("setup")
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::clippy_project_root;
|
||||
use indoc::indoc;
|
||||
use indoc::{indoc, writedoc};
|
||||
use std::fmt::Write as _;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::prelude::*;
|
||||
@ -10,6 +10,7 @@ struct LintData<'a> {
|
||||
pass: &'a str,
|
||||
name: &'a str,
|
||||
category: &'a str,
|
||||
ty: Option<&'a str>,
|
||||
project_root: PathBuf,
|
||||
}
|
||||
|
||||
@ -37,26 +38,44 @@ impl<T> Context for io::Result<T> {
|
||||
pub fn create(
|
||||
pass: Option<&String>,
|
||||
lint_name: Option<&String>,
|
||||
category: Option<&String>,
|
||||
category: Option<&str>,
|
||||
mut ty: Option<&str>,
|
||||
msrv: bool,
|
||||
) -> io::Result<()> {
|
||||
if category == Some("cargo") && ty.is_none() {
|
||||
// `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo`
|
||||
ty = Some("cargo");
|
||||
}
|
||||
|
||||
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"),
|
||||
category: category.expect("`category` argument is validated by clap"),
|
||||
ty,
|
||||
project_root: clippy_project_root(),
|
||||
};
|
||||
|
||||
create_lint(&lint, msrv).context("Unable to create lint implementation")?;
|
||||
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<()> {
|
||||
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);
|
||||
write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn create_test(lint: &LintData<'_>) -> io::Result<()> {
|
||||
@ -75,16 +94,22 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> {
|
||||
|
||||
if lint.category == "cargo" {
|
||||
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)?;
|
||||
|
||||
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 {
|
||||
let test_path = format!("tests/ui/{}.rs", lint.name);
|
||||
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<()> {
|
||||
@ -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 category = lint.category;
|
||||
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!(
|
||||
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,
|
||||
);
|
||||
let _ = write!(result, "{}", get_lint_declaration(&name_upper, category));
|
||||
|
||||
result.push_str(&if enable_msrv {
|
||||
format!(
|
||||
@ -312,6 +311,254 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
||||
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]
|
||||
fn test_camel_case() {
|
||||
let s = "a_lint";
|
||||
|
@ -824,10 +824,12 @@ macro_rules! match_tokens {
|
||||
}
|
||||
}
|
||||
|
||||
struct LintDeclSearchResult<'a> {
|
||||
token_kind: TokenKind,
|
||||
content: &'a str,
|
||||
range: Range<usize>,
|
||||
pub(crate) use match_tokens;
|
||||
|
||||
pub(crate) struct LintDeclSearchResult<'a> {
|
||||
pub token_kind: TokenKind,
|
||||
pub content: &'a str,
|
||||
pub range: Range<usize>,
|
||||
}
|
||||
|
||||
/// 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`
|
||||
/// regardless of whether good alternatives exist or not.
|
||||
/// 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`.
|
||||
/// 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).
|
||||
|
98
clippy_lints/src/assertions_on_result_states.rs
Normal file
98
clippy_lints/src/assertions_on_result_states.rs
Normal file
@ -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 clippy_utils::diagnostics::span_lint;
|
||||
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_span::DUMMY_SP;
|
||||
|
||||
mod common_metadata;
|
||||
mod feature_name;
|
||||
mod multiple_crate_versions;
|
||||
mod wildcard_dependencies;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks to see if all common metadata is defined in
|
||||
|
@ -24,7 +24,10 @@ declare_clippy_lint! {
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// 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"]
|
||||
|
@ -1028,9 +1028,10 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
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| {
|
||||
let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
|
||||
let sugg = if !snip_is_macro
|
||||
&& expr.precedence().order() < data.position.precedence()
|
||||
&& !has_enclosing_paren(&snip)
|
||||
&& (expr.precedence().order() < data.position.precedence() || calls_field)
|
||||
{
|
||||
format!("({})", snip)
|
||||
} else {
|
||||
|
@ -9,7 +9,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{sym, BytePos, Span};
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -85,22 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
||||
ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string,
|
||||
_ => false,
|
||||
};
|
||||
let sugg = if format_args.format_string_span.contains(value.span) {
|
||||
// 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 {
|
||||
let sugg = if is_new_string {
|
||||
snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
|
||||
} else {
|
||||
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
|
||||
if let ExprKind::MethodCall(path, [ref self_arg, ..], _) = expr.kind;
|
||||
// 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
|
||||
// separately)
|
||||
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(approx_const::APPROX_CONSTANT),
|
||||
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(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
||||
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::NEW_RET_NO_SELF),
|
||||
LintId::of(methods::NO_EFFECT_REPLACE),
|
||||
LintId::of(methods::OBFUSCATED_IF_ELSE),
|
||||
LintId::of(methods::OK_EXPECT),
|
||||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||
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_INTEL_SYNTAX,
|
||||
assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
|
||||
assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES,
|
||||
async_yields_async::ASYNC_YIELDS_ASYNC,
|
||||
attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
@ -330,6 +331,7 @@ store.register_lints(&[
|
||||
methods::NEEDLESS_SPLITN,
|
||||
methods::NEW_RET_NO_SELF,
|
||||
methods::NO_EFFECT_REPLACE,
|
||||
methods::OBFUSCATED_IF_ELSE,
|
||||
methods::OK_EXPECT,
|
||||
methods::OPTION_AS_REF_DEREF,
|
||||
methods::OPTION_FILTER_MAP,
|
||||
@ -417,6 +419,7 @@ store.register_lints(&[
|
||||
only_used_in_recursion::ONLY_USED_IN_RECURSION,
|
||||
open_options::NONSENSICAL_OPEN_OPTIONS,
|
||||
operators::ABSURD_EXTREME_COMPARISONS,
|
||||
operators::ARITHMETIC,
|
||||
operators::ASSIGN_OP_PATTERN,
|
||||
operators::BAD_BIT_MASK,
|
||||
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(module_style::MOD_MODULE_FILES),
|
||||
LintId::of(module_style::SELF_NAMED_MODULE_FILES),
|
||||
LintId::of(operators::ARITHMETIC),
|
||||
LintId::of(operators::FLOAT_ARITHMETIC),
|
||||
LintId::of(operators::FLOAT_CMP_CONST),
|
||||
LintId::of(operators::INTEGER_ARITHMETIC),
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
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(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
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::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(methods::NEW_RET_NO_SELF),
|
||||
LintId::of(methods::OBFUSCATED_IF_ELSE),
|
||||
LintId::of(methods::OK_EXPECT),
|
||||
LintId::of(methods::OPTION_MAP_OR_NONE),
|
||||
LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
|
||||
|
@ -174,6 +174,7 @@ mod as_conversions;
|
||||
mod as_underscore;
|
||||
mod asm_syntax;
|
||||
mod assertions_on_constants;
|
||||
mod assertions_on_result_states;
|
||||
mod async_yields_async;
|
||||
mod attrs;
|
||||
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));
|
||||
}
|
||||
|
||||
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::author::Author));
|
||||
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(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_result_states::AssertionsOnResultStates));
|
||||
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(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(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(exit::Exit));
|
||||
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;
|
||||
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(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`
|
||||
}
|
||||
|
||||
|
@ -460,7 +460,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
||||
let mut sub_visitor = RefVisitor::new(self.cx);
|
||||
sub_visitor.visit_fn_decl(decl);
|
||||
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
|
||||
return;
|
||||
},
|
||||
TyKind::TraitObject(bounds, ref lt, _) => {
|
||||
if !lt.is_elided() {
|
||||
@ -469,7 +468,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
||||
for bound in bounds {
|
||||
self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
|
||||
}
|
||||
return;
|
||||
},
|
||||
_ => 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 infallible_destructuring_match;
|
||||
mod manual_map;
|
||||
@ -31,6 +21,16 @@ mod single_match;
|
||||
mod try_err;
|
||||
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! {
|
||||
/// ### What it does
|
||||
/// Checks for matches with a single arm where an `if let`
|
||||
@ -793,18 +793,13 @@ declare_clippy_lint! {
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// # use std::sync::Mutex;
|
||||
///
|
||||
/// # struct State {}
|
||||
///
|
||||
/// # impl State {
|
||||
/// # fn foo(&self) -> bool {
|
||||
/// # true
|
||||
/// # }
|
||||
///
|
||||
/// # fn bar(&self) {}
|
||||
/// # }
|
||||
///
|
||||
///
|
||||
/// let mutex = Mutex::new(State {});
|
||||
///
|
||||
/// match mutex.lock().unwrap().foo() {
|
||||
@ -815,22 +810,17 @@ declare_clippy_lint! {
|
||||
/// };
|
||||
///
|
||||
/// println!("All done!");
|
||||
///
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::sync::Mutex;
|
||||
///
|
||||
/// # struct State {}
|
||||
///
|
||||
/// # impl State {
|
||||
/// # fn foo(&self) -> bool {
|
||||
/// # true
|
||||
/// # }
|
||||
///
|
||||
/// # fn bar(&self) {}
|
||||
/// # }
|
||||
///
|
||||
/// let mutex = Mutex::new(State {});
|
||||
///
|
||||
/// let is_foo = mutex.lock().unwrap().foo();
|
||||
|
@ -14,7 +14,7 @@ use super::INEFFICIENT_TO_STRING;
|
||||
/// Checks for the `INEFFICIENT_TO_STRING` lint
|
||||
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
|
||||
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 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);
|
||||
|
@ -46,6 +46,7 @@ mod map_unwrap_or;
|
||||
mod needless_option_as_deref;
|
||||
mod needless_option_take;
|
||||
mod no_effect_replace;
|
||||
mod obfuscated_if_else;
|
||||
mod ok_expect;
|
||||
mod option_as_ref_deref;
|
||||
mod option_map_or_none;
|
||||
@ -2263,6 +2264,35 @@ declare_clippy_lint! {
|
||||
"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 {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Option<RustcVersion>,
|
||||
@ -2364,6 +2394,7 @@ impl_lint_pass!(Methods => [
|
||||
IS_DIGIT_ASCII_RADIX,
|
||||
NEEDLESS_OPTION_TAKE,
|
||||
NO_EFFECT_REPLACE,
|
||||
OBFUSCATED_IF_ELSE,
|
||||
]);
|
||||
|
||||
/// 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)) => {
|
||||
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) {
|
||||
|
42
clippy_lints/src/methods/obfuscated_if_else.rs
Normal file
42
clippy_lints/src/methods/obfuscated_if_else.rs
Normal file
@ -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
|
||||
/// 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
|
||||
/// ```rust
|
||||
/// struct Foo<A, B> {
|
||||
@ -53,14 +58,15 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
|
||||
if !generic_args.args.is_empty();
|
||||
then {
|
||||
// get the name and span of the generic parameters in the Impl
|
||||
let impl_params = generic_args.args.iter()
|
||||
.filter_map(|p|
|
||||
let mut impl_params = Vec::new();
|
||||
for p in generic_args.args.iter() {
|
||||
match p {
|
||||
GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
|
||||
Some((path.segments[0].ident.to_string(), path.span)),
|
||||
_ => None,
|
||||
}
|
||||
);
|
||||
impl_params.push((path.segments[0].ident.to_string(), path.span)),
|
||||
GenericArg::Type(_) => return,
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
||||
// find the type that the Impl is for
|
||||
// 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();
|
||||
|
||||
let type_name = segment.ident;
|
||||
for (i, (impl_param_name, impl_param_span)) in impl_params.enumerate() {
|
||||
if mismatch_param_name(i, &impl_param_name, &type_param_names_hashmap) {
|
||||
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) {
|
||||
let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order",
|
||||
type_name, impl_param_name);
|
||||
let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params",
|
||||
@ -92,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
MISMATCHING_TYPE_PARAM_ORDER,
|
||||
impl_param_span,
|
||||
*impl_param_span,
|
||||
&msg,
|
||||
None,
|
||||
&help
|
||||
|
@ -251,14 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
|
||||
if let ItemKind::Const(hir_ty, body_id) = it.kind {
|
||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
||||
if !macro_backtrace(it.span).last().map_or(false, |macro_call| {
|
||||
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)
|
||||
{
|
||||
if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
|
||||
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
clippy_lints/src/operators/arithmetic.rs
Normal file
119
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::intravisit::{walk_expr, Visitor};
|
||||
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;
|
||||
|
||||
@ -29,6 +33,16 @@ pub(super) fn check<'tcx>(
|
||||
.map_or(true, |t| t.path.res.def_id() != trait_id);
|
||||
if implements_trait(cx, ty, trait_id, &[rty.into()]);
|
||||
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(
|
||||
cx,
|
||||
ASSIGN_OP_PATTERN,
|
||||
@ -99,3 +113,69 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
|
||||
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 assign_op_pattern;
|
||||
mod bit_mask;
|
||||
@ -25,6 +21,12 @@ mod ptr_eq;
|
||||
mod self_assignment;
|
||||
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! {
|
||||
/// ### What it does
|
||||
/// 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"
|
||||
}
|
||||
|
||||
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! {
|
||||
/// ### What it does
|
||||
/// Checks for integer arithmetic operations which could overflow or panic.
|
||||
@ -747,6 +785,7 @@ pub struct Operators {
|
||||
}
|
||||
impl_lint_pass!(Operators => [
|
||||
ABSURD_EXTREME_COMPARISONS,
|
||||
ARITHMETIC,
|
||||
INTEGER_ARITHMETIC,
|
||||
FLOAT_ARITHMETIC,
|
||||
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 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 let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
|
||||
if let PatKind::Binding(annot, bind_id, ident, _) = fields[0].kind;
|
||||
if let PatKind::TupleStruct(ref path1, [field], None) = let_pat.kind;
|
||||
if let PatKind::Binding(annot, bind_id, ident, _) = field.kind;
|
||||
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);
|
||||
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 rustc_hir::{def::Res, HirId, Path, PathSegment};
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{sym, symbol::kw, Symbol};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{sym, symbol::kw, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -63,7 +63,7 @@ declare_clippy_lint! {
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// ### Example
|
||||
@ -81,39 +81,55 @@ declare_clippy_lint! {
|
||||
"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 {
|
||||
fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
|
||||
// std_instead_of_core
|
||||
check_path(cx, path, sym::std, sym::core, STD_INSTEAD_OF_CORE);
|
||||
// std_instead_of_alloc
|
||||
check_path(cx, path, sym::std, sym::alloc, STD_INSTEAD_OF_ALLOC);
|
||||
// alloc_instead_of_core
|
||||
check_path(cx, path, sym::alloc, sym::core, ALLOC_INSTEAD_OF_CORE);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_crate: Symbol, lint: &'static Lint) {
|
||||
if_chain! {
|
||||
// check if path resolves to the suggested crate.
|
||||
if let Res::Def(_, def_id) = path.res;
|
||||
if suggested_crate == cx.tcx.crate_name(def_id.krate);
|
||||
|
||||
// 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);
|
||||
|
||||
// check if the path matches the crate we want to suggest the other path for.
|
||||
if krate == path_root_segment.ident.name;
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
lint,
|
||||
path.span,
|
||||
&format!("used import from `{}` instead of `{}`", krate, suggested_crate),
|
||||
None,
|
||||
&format!("consider importing the item from `{}`", suggested_crate),
|
||||
);
|
||||
if let Res::Def(_, def_id) = path.res
|
||||
&& let Some(first_segment) = get_first_segment(path)
|
||||
{
|
||||
let (lint, msg, help) = match first_segment.ident.name {
|
||||
sym::std => match cx.tcx.crate_name(def_id.krate) {
|
||||
sym::core => (
|
||||
STD_INSTEAD_OF_CORE,
|
||||
"used import from `std` instead of `core`",
|
||||
"consider importing the item from `core`",
|
||||
),
|
||||
sym::alloc => (
|
||||
STD_INSTEAD_OF_ALLOC,
|
||||
"used import from `std` instead of `alloc`",
|
||||
"consider importing the item from `alloc`",
|
||||
),
|
||||
_ => {
|
||||
self.prev_span = path.span;
|
||||
return;
|
||||
},
|
||||
},
|
||||
sym::alloc => {
|
||||
if cx.tcx.crate_name(def_id.krate) == sym::core {
|
||||
(
|
||||
ALLOC_INSTEAD_OF_CORE,
|
||||
"used import from `alloc` instead of `core`",
|
||||
"consider importing the item from `core`",
|
||||
)
|
||||
} 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`]
|
||||
/// is returned.
|
||||
fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
|
||||
let segment = path.segments.first()?;
|
||||
|
||||
// A global path will have PathRoot as the first segment. In this case, return the segment after.
|
||||
if segment.ident.name == kw::PathRoot {
|
||||
path.segments.get(1)
|
||||
} else {
|
||||
Some(segment)
|
||||
match path.segments {
|
||||
// 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),
|
||||
[x, ..] => Some(x),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -394,7 +394,7 @@ impl<'tcx> LateLintPass<'tcx> for StrToString {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
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);
|
||||
if let ty::Ref(_, ty, ..) = ty.kind();
|
||||
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<'_>) {
|
||||
if_chain! {
|
||||
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);
|
||||
if is_type_diagnostic_item(cx, ty, sym::String);
|
||||
then {
|
||||
|
@ -3,7 +3,7 @@ use clippy_utils::visitors::is_local_used;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind};
|
||||
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! {
|
||||
/// ### What it does
|
||||
@ -35,7 +35,19 @@ declare_clippy_lint! {
|
||||
"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 {
|
||||
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 assoc_item.fn_has_self_parameter;
|
||||
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);
|
||||
if let [self_param, ..] = body.params;
|
||||
if !is_local_used(cx, body, self_param.pat.hir_id);
|
||||
|
@ -191,7 +191,11 @@ macro_rules! 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.
|
||||
(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() {
|
||||
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
|
||||
.inner()
|
||||
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
|
||||
.to_owned()
|
||||
.chunks(4)
|
||||
.map(|chunk| {
|
||||
Some(Constant::F32(f32::from_le_bytes(
|
||||
chunk.try_into().expect("this shouldn't happen"),
|
||||
)))
|
||||
})
|
||||
.array_chunks::<4>()
|
||||
.map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
|
||||
.collect::<Option<Vec<Constant>>>()
|
||||
.map(Constant::Vec),
|
||||
_ => 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
|
||||
.inner()
|
||||
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
|
||||
.to_owned()
|
||||
.chunks(8)
|
||||
.map(|chunk| {
|
||||
Some(Constant::F64(f64::from_le_bytes(
|
||||
chunk.try_into().expect("this shouldn't happen"),
|
||||
)))
|
||||
})
|
||||
.array_chunks::<8>()
|
||||
.map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
|
||||
.collect::<Option<Vec<Constant>>>()
|
||||
.map(Constant::Vec),
|
||||
_ => None,
|
||||
|
@ -127,9 +127,6 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
|
||||
/// Checks whether two blocks are the same.
|
||||
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) {
|
||||
([], None, [], None) => {
|
||||
// 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 {
|
||||
if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
|
||||
macro_backtrace(expr.span).last().map_or(false, |macro_call| {
|
||||
matches!(
|
||||
&self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
|
||||
Some(sym::todo_macro | sym::unimplemented_macro)
|
||||
)
|
||||
}) {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
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),
|
||||
_ => 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 {
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![feature(array_chunks)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![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();
|
||||
|
||||
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 mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
|
||||
let value = map.entry(module);
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2022-07-15"
|
||||
channel = "nightly-2022-07-28"
|
||||
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);
|
||||
assert!(
|
||||
rs_path.exists(),
|
||||
"`{}` does not exists",
|
||||
"`{}` does not exist",
|
||||
rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
|
||||
);
|
||||
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
|
||||
#[must_use]
|
||||
struct VarGuard {
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "cargo_common_metadata"
|
||||
name = "cargo_common_metadata_fail"
|
||||
version = "0.1.0"
|
||||
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`
|
||||
|
||||
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
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "cargo_common_metadata"
|
||||
name = "cargo_common_metadata_fail_publish"
|
||||
version = "0.1.0"
|
||||
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`
|
||||
|
||||
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
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "cargo_common_metadata"
|
||||
name = "cargo_common_metadata_fail_publish_true"
|
||||
version = "0.1.0"
|
||||
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`
|
||||
|
||||
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
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "cargo_common_metadata"
|
||||
name = "cargo_common_metadata_pass"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
description = "A test package for the cargo_common_metadata lint"
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "cargo_common_metadata"
|
||||
name = "cargo_common_metadata_pass_publish_empty"
|
||||
version = "0.1.0"
|
||||
publish = []
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "cargo_common_metadata"
|
||||
name = "cargo_common_metadata_pass_publish_false"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "fail-both-diff"
|
||||
version = "0.1.0"
|
||||
rust-version = "1.56"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "fail-both-same"
|
||||
version = "0.1.0"
|
||||
rust-version = "1.57.0"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "fail-cargo"
|
||||
version = "0.1.0"
|
||||
rust-version = "1.56.1"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
[package]
|
||||
name = "fail-clippy"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "fail-file-attr"
|
||||
version = "0.1.0"
|
||||
rust-version = "1.13"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "fail-both-same"
|
||||
name = "pass-both-same"
|
||||
version = "0.1.0"
|
||||
rust-version = "1.13.0"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "fail-cargo"
|
||||
name = "pass-cargo"
|
||||
version = "0.1.0"
|
||||
rust-version = "1.13.0"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
[package]
|
||||
name = "fail-clippy"
|
||||
name = "pass-clippy"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "fail-file-attr"
|
||||
name = "pass-file-attr"
|
||||
version = "0.1.0"
|
||||
rust-version = "1.59"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "warn-both-diff"
|
||||
version = "0.1.0"
|
||||
rust-version = "1.56.0"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "fail"
|
||||
name = "fail-mod"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "fail"
|
||||
name = "fail-no-mod"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "fail"
|
||||
name = "pass-mod"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "pass"
|
||||
name = "pass-no-mod"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "no_warn"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "warn"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
// ignore-windows
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "cargo_common_metadata"
|
||||
name = "multiple_crate_versions"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
|
@ -17,7 +17,7 @@ LL | #![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`
|
||||
= 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
|
||||
--> $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`
|
||||
= 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
|
||||
--> $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)]`
|
||||
= 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
|
||||
--> $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`
|
||||
= 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
|
||||
|
||||
|
@ -15,7 +15,7 @@ note: the lint level is defined here
|
||||
LL | #![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
|
||||
|
||||
|
@ -56,7 +56,7 @@ 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!`
|
||||
--> $DIR/if_chain_style.rs:40:9
|
||||
|
24
tests/ui-toml/arithmetic_allowed/arithmetic_allowed.rs
Normal file
24
tests/ui-toml/arithmetic_allowed/arithmetic_allowed.rs
Normal file
@ -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;
|
||||
}
|
1
tests/ui-toml/arithmetic_allowed/clippy.toml
Normal file
1
tests/ui-toml/arithmetic_allowed/clippy.toml
Normal file
@ -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-unwrap-in-tests
|
||||
allowed-scripts
|
||||
arithmetic-allowed
|
||||
array-size-threshold
|
||||
avoid-breaking-exported-api
|
||||
await-holding-invalid-types
|
||||
|
27
tests/ui/arithmetic.fixed
Normal file
27
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
tests/ui/arithmetic.rs
Normal file
27
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
tests/ui/assertions_on_result_states.fixed
Normal file
69
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
tests/ui/assertions_on_result_states.rs
Normal file
69
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
tests/ui/assertions_on_result_states.stderr
Normal file
40
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
|
||||
|
||||
use core::num::Wrapping;
|
||||
|
||||
#[allow(dead_code, unused_assignments)]
|
||||
#[warn(clippy::assign_op_pattern)]
|
||||
fn main() {
|
||||
@ -18,4 +20,13 @@ fn main() {
|
||||
a = 6 << a;
|
||||
let mut s = String::new();
|
||||
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
|
||||
|
||||
use core::num::Wrapping;
|
||||
|
||||
#[allow(dead_code, unused_assignments)]
|
||||
#[warn(clippy::assign_op_pattern)]
|
||||
fn main() {
|
||||
@ -18,4 +20,13 @@ fn main() {
|
||||
a = 6 << a;
|
||||
let mut s = String::new();
|
||||
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
|
||||
--> $DIR/assign_ops.rs:7:5
|
||||
--> $DIR/assign_ops.rs:9:5
|
||||
|
|
||||
LL | a = 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`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> $DIR/assign_ops.rs:8:5
|
||||
--> $DIR/assign_ops.rs:10:5
|
||||
|
|
||||
LL | a = 1 + a;
|
||||
| ^^^^^^^^^ help: replace it with: `a += 1`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> $DIR/assign_ops.rs:9:5
|
||||
--> $DIR/assign_ops.rs:11:5
|
||||
|
|
||||
LL | a = a - 1;
|
||||
| ^^^^^^^^^ help: replace it with: `a -= 1`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> $DIR/assign_ops.rs:10:5
|
||||
--> $DIR/assign_ops.rs:12:5
|
||||
|
|
||||
LL | a = a * 99;
|
||||
| ^^^^^^^^^^ help: replace it with: `a *= 99`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> $DIR/assign_ops.rs:11:5
|
||||
--> $DIR/assign_ops.rs:13:5
|
||||
|
|
||||
LL | a = 42 * a;
|
||||
| ^^^^^^^^^^ help: replace it with: `a *= 42`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> $DIR/assign_ops.rs:12:5
|
||||
--> $DIR/assign_ops.rs:14:5
|
||||
|
|
||||
LL | a = a / 2;
|
||||
| ^^^^^^^^^ help: replace it with: `a /= 2`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> $DIR/assign_ops.rs:13:5
|
||||
--> $DIR/assign_ops.rs:15:5
|
||||
|
|
||||
LL | a = a % 5;
|
||||
| ^^^^^^^^^ help: replace it with: `a %= 5`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> $DIR/assign_ops.rs:14:5
|
||||
--> $DIR/assign_ops.rs:16:5
|
||||
|
|
||||
LL | a = a & 1;
|
||||
| ^^^^^^^^^ help: replace it with: `a &= 1`
|
||||
|
||||
error: manual implementation of an assign operation
|
||||
--> $DIR/assign_ops.rs:20:5
|
||||
--> $DIR/assign_ops.rs:22:5
|
||||
|
|
||||
LL | s = 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
tests/ui/crashes/ice-9238.rs
Normal file
12
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
tests/ui/crashes/ice-9242.rs
Normal file
8
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-windows
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: recursing into entrypoint `a`
|
||||
--> $DIR/entrypoint_recursion.rs:11:5
|
||||
--> $DIR/entrypoint_recursion.rs:10:5
|
||||
|
|
||||
LL | a();
|
||||
| ^
|
||||
|
@ -31,9 +31,25 @@ const NO_ANN: &dyn Display = &70;
|
||||
static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
|
||||
//^ there should be no lints on this line
|
||||
|
||||
// issue #8493
|
||||
thread_local! {
|
||||
static THREAD_LOCAL: Cell<i32> = const { Cell::new(0) };
|
||||
mod issue_8493 {
|
||||
use std::cell::Cell;
|
||||
|
||||
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() {}
|
||||
|
@ -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)
|
||||
|
||||
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 _ = format!("{x:?}"); // Don't lint on debug
|
||||
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:?}"); // Don't lint on debug
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ 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)]
|
||||
#![allow(clippy::blacklisted_name)]
|
||||
#![allow(clippy::blacklisted_name, clippy::diverging_sub_expression)]
|
||||
|
||||
fn bar<T>(_: T) {}
|
||||
fn foo() -> bool {
|
||||
@ -227,4 +227,12 @@ fn main() {
|
||||
Some(Bar { y: 0, x: 5, .. }) => 1,
|
||||
_ => 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,
|
||||
{
|
||||
}
|
||||
|
||||
// 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)]
|
||||
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)]
|
||||
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();
|
||||
| ^^^^^ 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
tests/ui/obfuscated_if_else.fixed
Normal file
7
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
tests/ui/obfuscated_if_else.rs
Normal file
7
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
tests/ui/obfuscated_if_else.stderr
Normal file
10
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;
|
||||
// Absolute path
|
||||
use ::std::hash::Hash;
|
||||
// Don't lint on `env` macro
|
||||
use std::env;
|
||||
|
||||
// Multiple imports
|
||||
use std::fmt::{Debug, Result};
|
||||
@ -20,10 +22,14 @@ fn std_instead_of_core() {
|
||||
// Types
|
||||
let cell = std::cell::Cell::new(8u32);
|
||||
let cell_absolute = ::std::cell::Cell::new(8u32);
|
||||
|
||||
let _ = std::env!("PATH");
|
||||
}
|
||||
|
||||
#[warn(clippy::std_instead_of_alloc)]
|
||||
fn std_instead_of_alloc() {
|
||||
// Only lint once.
|
||||
use std::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