Merge commit '3c7e7dbc1583a0b06df5bd7623dd354a4debd23d' into clippyup

This commit is contained in:
Philipp Krones 2022-07-28 19:08:22 +02:00
commit 7a782537b1
No known key found for this signature in database
GPG Key ID: 1CA0DF2AF59D68A5
102 changed files with 1527 additions and 298 deletions

View File

@ -3437,9 +3437,11 @@ Released 2018-09-13
[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range [`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
[`arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore [`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
[`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states
[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async [`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
@ -3793,6 +3795,7 @@ Released 2018-09-13
[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces [`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
[`obfuscated_if_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#obfuscated_if_else
[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes [`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect [`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion [`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion

View File

@ -32,6 +32,7 @@ compiletest_rs = { version = "0.8", features = ["tmp"] }
tester = "0.9" tester = "0.9"
regex = "1.5" regex = "1.5"
toml = "0.5" toml = "0.5"
walkdir = "2.3"
# This is used by the `collect-metadata` alias. # This is used by the `collect-metadata` alias.
filetime = "0.2" filetime = "0.2"

View File

@ -1,7 +1,7 @@
# GitHub Actions # GitHub Actions
On the GitHub hosted runners, Clippy from the latest stable Rust version comes GitHub hosted runners using the latest stable version of Rust have Clippy pre-installed.
pre-installed. So all you have to do is to run `cargo clippy`. It is as simple as running `cargo clippy` to run lints against the codebase.
```yml ```yml
on: push on: push
@ -15,7 +15,7 @@ jobs:
clippy_check: clippy_check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v3
- name: Run Clippy - name: Run Clippy
run: cargo clippy --all-targets --all-features run: cargo clippy --all-targets --all-features
``` ```

View File

@ -10,6 +10,10 @@ because that's clearly a non-descriptive name.
- [Adding a new lint](#adding-a-new-lint) - [Adding a new lint](#adding-a-new-lint)
- [Setup](#setup) - [Setup](#setup)
- [Getting Started](#getting-started) - [Getting Started](#getting-started)
- [Defining Our Lint](#defining-our-lint)
- [Standalone](#standalone)
- [Specific Type](#specific-type)
- [Tests Location](#tests-location)
- [Testing](#testing) - [Testing](#testing)
- [Cargo lints](#cargo-lints) - [Cargo lints](#cargo-lints)
- [Rustfix tests](#rustfix-tests) - [Rustfix tests](#rustfix-tests)
@ -36,17 +40,38 @@ See the [Basics](basics.md#get-the-code) documentation.
## Getting Started ## Getting Started
There is a bit of boilerplate code that needs to be set up when creating a new There is a bit of boilerplate code that needs to be set up when creating a new
lint. Fortunately, you can use the clippy dev tools to handle this for you. We lint. Fortunately, you can use the Clippy dev tools to handle this for you. We
are naming our new lint `foo_functions` (lints are generally written in snake are naming our new lint `foo_functions` (lints are generally written in snake
case), and we don't need type information so it will have an early pass type case), and we don't need type information, so it will have an early pass type
(more on this later on). If you're not sure if the name you chose fits the lint, (more on this later). If you're unsure if the name you chose fits the lint,
take a look at our [lint naming guidelines][lint_naming]. To get started on this take a look at our [lint naming guidelines][lint_naming].
lint you can run `cargo dev new_lint --name=foo_functions --pass=early
--category=pedantic` (category will default to nursery if not provided). This ## Defining Our Lint
command will create two files: `tests/ui/foo_functions.rs` and To get started, there are two ways to define our lint.
`clippy_lints/src/foo_functions.rs`, as well as [registering the
lint](#lint-registration). For cargo lints, two project hierarchies (fail/pass) ### Standalone
will be created by default under `tests/ui-cargo`. Command: `cargo dev new_lint --name=foo_functions --pass=early --category=pedantic`
(category will default to nursery if not provided)
This command will create a new file: `clippy_lints/src/foo_functions.rs`, as well
as [register the lint](#lint-registration).
### Specific Type
Command: `cargo dev new_lint --name=foo_functions --type=functions --category=pedantic`
This command will create a new file: `clippy_lints/src/{type}/foo_functions.rs`.
Notice how this command has a `--type` flag instead of `--pass`. Unlike a standalone
definition, this lint won't be registered in the traditional sense. Instead, you will
call your lint from within the type's lint pass, found in `clippy_lints/src/{type}/mod.rs`.
A "type" is just the name of a directory in `clippy_lints/src`, like `functions` in
the example command. These are groupings of lints with common behaviors, so if your
lint falls into one, it would be best to add it to that type.
### Tests Location
Both commands will create a file: `tests/ui/foo_functions.rs`. For cargo lints,
two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`.
Next, we'll open up these files and add our lint! Next, we'll open up these files and add our lint!

View File

@ -25,7 +25,7 @@ instructions for other options.
## Make changes ## Make changes
The book's The book's
[src](https://github.com/joshrotenberg/rust-clippy/tree/clippy_guide/book/src) [src](https://github.com/rust-lang/rust-clippy/tree/master/book/src)
directory contains all of the markdown files used to generate the book. If you directory contains all of the markdown files used to generate the book. If you
want to see your changes in real time, you can use the mdbook `serve` command to want to see your changes in real time, you can use the mdbook `serve` command to
run a web server locally that will automatically update changes as they are run a web server locally that will automatically update changes as they are

View File

@ -13,7 +13,7 @@ pub enum CliError {
IoError(io::Error), IoError(io::Error),
RustfmtNotInstalled, RustfmtNotInstalled,
WalkDirError(walkdir::Error), WalkDirError(walkdir::Error),
RaSetupActive, IntellijSetupActive,
} }
impl From<io::Error> for CliError { impl From<io::Error> for CliError {
@ -48,7 +48,7 @@ pub fn run(check: bool, verbose: bool) {
.expect("Failed to read clippy Cargo.toml") .expect("Failed to read clippy Cargo.toml")
.contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]") .contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
{ {
return Err(CliError::RaSetupActive); return Err(CliError::IntellijSetupActive);
} }
rustfmt_test(context)?; rustfmt_test(context)?;
@ -93,11 +93,11 @@ pub fn run(check: bool, verbose: bool) {
CliError::WalkDirError(err) => { CliError::WalkDirError(err) => {
eprintln!("error: {}", err); eprintln!("error: {}", err);
}, },
CliError::RaSetupActive => { CliError::IntellijSetupActive => {
eprintln!( eprintln!(
"error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`. "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.
Not formatting because that would format the local repo as well! Not formatting because that would format the local repo as well!
Please revert the changes to Cargo.tomls first." Please revert the changes to Cargo.tomls with `cargo dev remove intellij`."
); );
}, },
} }

View File

@ -36,7 +36,8 @@ fn main() {
match new_lint::create( match new_lint::create(
matches.get_one::<String>("pass"), matches.get_one::<String>("pass"),
matches.get_one::<String>("name"), matches.get_one::<String>("name"),
matches.get_one::<String>("category"), matches.get_one::<String>("category").map(String::as_str),
matches.get_one::<String>("type").map(String::as_str),
matches.contains_id("msrv"), matches.contains_id("msrv"),
) { ) {
Ok(_) => update_lints::update(update_lints::UpdateMode::Change), Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
@ -157,7 +158,8 @@ fn get_clap_config() -> ArgMatches {
.help("Specify whether the lint runs during the early or late pass") .help("Specify whether the lint runs during the early or late pass")
.takes_value(true) .takes_value(true)
.value_parser([PossibleValue::new("early"), PossibleValue::new("late")]) .value_parser([PossibleValue::new("early"), PossibleValue::new("late")])
.required(true), .conflicts_with("type")
.required_unless_present("type"),
Arg::new("name") Arg::new("name")
.short('n') .short('n')
.long("name") .long("name")
@ -183,6 +185,11 @@ fn get_clap_config() -> ArgMatches {
PossibleValue::new("internal_warn"), PossibleValue::new("internal_warn"),
]) ])
.takes_value(true), .takes_value(true),
Arg::new("type")
.long("type")
.help("What directory the lint belongs in")
.takes_value(true)
.required(false),
Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"), Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"),
]), ]),
Command::new("setup") Command::new("setup")

View File

@ -1,5 +1,5 @@
use crate::clippy_project_root; use crate::clippy_project_root;
use indoc::indoc; use indoc::{indoc, writedoc};
use std::fmt::Write as _; use std::fmt::Write as _;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::prelude::*; use std::io::prelude::*;
@ -10,6 +10,7 @@ struct LintData<'a> {
pass: &'a str, pass: &'a str,
name: &'a str, name: &'a str,
category: &'a str, category: &'a str,
ty: Option<&'a str>,
project_root: PathBuf, project_root: PathBuf,
} }
@ -37,26 +38,44 @@ impl<T> Context for io::Result<T> {
pub fn create( pub fn create(
pass: Option<&String>, pass: Option<&String>,
lint_name: Option<&String>, lint_name: Option<&String>,
category: Option<&String>, category: Option<&str>,
mut ty: Option<&str>,
msrv: bool, msrv: bool,
) -> io::Result<()> { ) -> io::Result<()> {
if category == Some("cargo") && ty.is_none() {
// `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo`
ty = Some("cargo");
}
let lint = LintData { let lint = LintData {
pass: pass.expect("`pass` argument is validated by clap"), pass: pass.map_or("", String::as_str),
name: lint_name.expect("`name` argument is validated by clap"), name: lint_name.expect("`name` argument is validated by clap"),
category: category.expect("`category` argument is validated by clap"), category: category.expect("`category` argument is validated by clap"),
ty,
project_root: clippy_project_root(), project_root: clippy_project_root(),
}; };
create_lint(&lint, msrv).context("Unable to create lint implementation")?; create_lint(&lint, msrv).context("Unable to create lint implementation")?;
create_test(&lint).context("Unable to create a test for the new lint")?; create_test(&lint).context("Unable to create a test for the new lint")?;
add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")
if lint.ty.is_none() {
add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?;
}
Ok(())
} }
fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
let lint_contents = get_lint_file_contents(lint, enable_msrv); if let Some(ty) = lint.ty {
create_lint_for_ty(lint, enable_msrv, ty)
} else {
let lint_contents = get_lint_file_contents(lint, enable_msrv);
let lint_path = format!("clippy_lints/src/{}.rs", lint.name);
write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?;
println!("Generated lint file: `{}`", lint_path);
let lint_path = format!("clippy_lints/src/{}.rs", lint.name); Ok(())
write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes()) }
} }
fn create_test(lint: &LintData<'_>) -> io::Result<()> { fn create_test(lint: &LintData<'_>) -> io::Result<()> {
@ -75,16 +94,22 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> {
if lint.category == "cargo" { if lint.category == "cargo" {
let relative_test_dir = format!("tests/ui-cargo/{}", lint.name); let relative_test_dir = format!("tests/ui-cargo/{}", lint.name);
let test_dir = lint.project_root.join(relative_test_dir); let test_dir = lint.project_root.join(&relative_test_dir);
fs::create_dir(&test_dir)?; fs::create_dir(&test_dir)?;
create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?; create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?;
create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint") create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")?;
println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`");
} else { } else {
let test_path = format!("tests/ui/{}.rs", lint.name); let test_path = format!("tests/ui/{}.rs", lint.name);
let test_contents = get_test_file_contents(lint.name, None); let test_contents = get_test_file_contents(lint.name, None);
write_file(lint.project_root.join(test_path), test_contents) write_file(lint.project_root.join(&test_path), test_contents)?;
println!("Generated test file: `{}`", test_path);
} }
Ok(())
} }
fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
@ -204,7 +229,6 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
}, },
}; };
let version = get_stabilization_version();
let lint_name = lint.name; let lint_name = lint.name;
let category = lint.category; let category = lint.category;
let name_camel = to_camel_case(lint.name); let name_camel = to_camel_case(lint.name);
@ -238,32 +262,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
) )
}); });
let _ = write!( let _ = write!(result, "{}", get_lint_declaration(&name_upper, category));
result,
indoc! {r#"
declare_clippy_lint! {{
/// ### What it does
///
/// ### Why is this bad?
///
/// ### Example
/// ```rust
/// // example code where clippy issues a warning
/// ```
/// Use instead:
/// ```rust
/// // example code which does not raise clippy warning
/// ```
#[clippy::version = "{version}"]
pub {name_upper},
{category},
"default lint description"
}}
"#},
version = version,
name_upper = name_upper,
category = category,
);
result.push_str(&if enable_msrv { result.push_str(&if enable_msrv {
format!( format!(
@ -312,6 +311,254 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
result result
} }
fn get_lint_declaration(name_upper: &str, category: &str) -> String {
format!(
indoc! {r#"
declare_clippy_lint! {{
/// ### What it does
///
/// ### Why is this bad?
///
/// ### Example
/// ```rust
/// // example code where clippy issues a warning
/// ```
/// Use instead:
/// ```rust
/// // example code which does not raise clippy warning
/// ```
#[clippy::version = "{version}"]
pub {name_upper},
{category},
"default lint description"
}}
"#},
version = get_stabilization_version(),
name_upper = name_upper,
category = category,
)
}
fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::Result<()> {
match ty {
"cargo" => assert_eq!(
lint.category, "cargo",
"Lints of type `cargo` must have the `cargo` category"
),
_ if lint.category == "cargo" => panic!("Lints of category `cargo` must have the `cargo` type"),
_ => {},
}
let ty_dir = lint.project_root.join(format!("clippy_lints/src/{}", ty));
assert!(
ty_dir.exists() && ty_dir.is_dir(),
"Directory `{}` does not exist!",
ty_dir.display()
);
let lint_file_path = ty_dir.join(format!("{}.rs", lint.name));
assert!(
!lint_file_path.exists(),
"File `{}` already exists",
lint_file_path.display()
);
let mod_file_path = ty_dir.join("mod.rs");
let context_import = setup_mod_file(&mod_file_path, lint)?;
let name_upper = lint.name.to_uppercase();
let mut lint_file_contents = String::new();
if enable_msrv {
let _ = writedoc!(
lint_file_contents,
r#"
use clippy_utils::{{meets_msrv, msrvs}};
use rustc_lint::{{{context_import}, LintContext}};
use rustc_semver::RustcVersion;
use super::{name_upper};
// TODO: Adjust the parameters as necessary
pub(super) fn check(cx: &{context_import}, msrv: Option<RustcVersion>) {{
if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
return;
}}
todo!();
}}
"#,
context_import = context_import,
name_upper = name_upper,
);
} else {
let _ = writedoc!(
lint_file_contents,
r#"
use rustc_lint::{{{context_import}, LintContext}};
use super::{name_upper};
// TODO: Adjust the parameters as necessary
pub(super) fn check(cx: &{context_import}) {{
todo!();
}}
"#,
context_import = context_import,
name_upper = name_upper,
);
}
write_file(lint_file_path.as_path(), lint_file_contents)?;
println!("Generated lint file: `clippy_lints/src/{}/{}.rs`", ty, lint.name);
println!(
"Be sure to add a call to `{}::check` in `clippy_lints/src/{}/mod.rs`!",
lint.name, ty
);
Ok(())
}
#[allow(clippy::too_many_lines)]
fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> {
use super::update_lints::{match_tokens, LintDeclSearchResult};
use rustc_lexer::TokenKind;
let lint_name_upper = lint.name.to_uppercase();
let mut file_contents = fs::read_to_string(path)?;
assert!(
!file_contents.contains(&lint_name_upper),
"Lint `{}` already defined in `{}`",
lint.name,
path.display()
);
let mut offset = 0usize;
let mut last_decl_curly_offset = None;
let mut lint_context = None;
let mut iter = rustc_lexer::tokenize(&file_contents).map(|t| {
let range = offset..offset + t.len;
offset = range.end;
LintDeclSearchResult {
token_kind: t.kind,
content: &file_contents[range.clone()],
range,
}
});
// Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl
while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) {
let mut iter = iter
.by_ref()
.filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
match content {
"declare_clippy_lint" => {
// matches `!{`
match_tokens!(iter, Bang OpenBrace);
if let Some(LintDeclSearchResult { range, .. }) =
iter.find(|result| result.token_kind == TokenKind::CloseBrace)
{
last_decl_curly_offset = Some(range.end);
}
},
"impl" => {
let mut token = iter.next();
match token {
// matches <'foo>
Some(LintDeclSearchResult {
token_kind: TokenKind::Lt,
..
}) => {
match_tokens!(iter, Lifetime { .. } Gt);
token = iter.next();
},
None => break,
_ => {},
}
if let Some(LintDeclSearchResult {
token_kind: TokenKind::Ident,
content,
..
}) = token
{
// Get the appropriate lint context struct
lint_context = match content {
"LateLintPass" => Some("LateContext"),
"EarlyLintPass" => Some("EarlyContext"),
_ => continue,
};
}
},
_ => {},
}
}
drop(iter);
let last_decl_curly_offset =
last_decl_curly_offset.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display()));
let lint_context =
lint_context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display()));
// Add the lint declaration to `mod.rs`
file_contents.replace_range(
// Remove the trailing newline, which should always be present
last_decl_curly_offset..=last_decl_curly_offset,
&format!("\n\n{}", get_lint_declaration(&lint_name_upper, lint.category)),
);
// Add the lint to `impl_lint_pass`/`declare_lint_pass`
let impl_lint_pass_start = file_contents.find("impl_lint_pass!").unwrap_or_else(|| {
file_contents
.find("declare_lint_pass!")
.unwrap_or_else(|| panic!("failed to find `impl_lint_pass`/`declare_lint_pass`"))
});
let mut arr_start = file_contents[impl_lint_pass_start..].find('[').unwrap_or_else(|| {
panic!("malformed `impl_lint_pass`/`declare_lint_pass`");
});
arr_start += impl_lint_pass_start;
let mut arr_end = file_contents[arr_start..]
.find(']')
.expect("failed to find `impl_lint_pass` terminator");
arr_end += arr_start;
let mut arr_content = file_contents[arr_start + 1..arr_end].to_string();
arr_content.retain(|c| !c.is_whitespace());
let mut new_arr_content = String::new();
for ident in arr_content
.split(',')
.chain(std::iter::once(&*lint_name_upper))
.filter(|s| !s.is_empty())
{
let _ = write!(new_arr_content, "\n {},", ident);
}
new_arr_content.push('\n');
file_contents.replace_range(arr_start + 1..arr_end, &new_arr_content);
// Just add the mod declaration at the top, it'll be fixed by rustfmt
file_contents.insert_str(0, &format!("mod {};\n", &lint.name));
let mut file = OpenOptions::new()
.write(true)
.truncate(true)
.open(path)
.context(format!("trying to open: `{}`", path.display()))?;
file.write_all(file_contents.as_bytes())
.context(format!("writing to file: `{}`", path.display()))?;
Ok(lint_context)
}
#[test] #[test]
fn test_camel_case() { fn test_camel_case() {
let s = "a_lint"; let s = "a_lint";

View File

@ -824,10 +824,12 @@ macro_rules! match_tokens {
} }
} }
struct LintDeclSearchResult<'a> { pub(crate) use match_tokens;
token_kind: TokenKind,
content: &'a str, pub(crate) struct LintDeclSearchResult<'a> {
range: Range<usize>, pub token_kind: TokenKind,
pub content: &'a str,
pub range: Range<usize>,
} }
/// Parse a source file looking for `declare_clippy_lint` macro invocations. /// Parse a source file looking for `declare_clippy_lint` macro invocations.

View File

@ -11,7 +11,7 @@ declare_clippy_lint! {
/// Note that this lint is specialized in linting *every single* use of `as` /// Note that this lint is specialized in linting *every single* use of `as`
/// regardless of whether good alternatives exist or not. /// regardless of whether good alternatives exist or not.
/// If you want more precise lints for `as`, please consider using these separate lints: /// If you want more precise lints for `as`, please consider using these separate lints:
/// `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`, /// `unnecessary_cast`, `cast_lossless/cast_possible_truncation/cast_possible_wrap/cast_precision_loss/cast_sign_loss`,
/// `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`. /// `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.
/// There is a good explanation the reason why this lint should work in this way and how it is useful /// There is a good explanation the reason why this lint should work in this way and how it is useful
/// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122). /// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).

View 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, &[]))
}

View File

@ -1,3 +1,8 @@
mod common_metadata;
mod feature_name;
mod multiple_crate_versions;
mod wildcard_dependencies;
use cargo_metadata::MetadataCommand; use cargo_metadata::MetadataCommand;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_lint_allowed; use clippy_utils::is_lint_allowed;
@ -6,11 +11,6 @@ use rustc_lint::{LateContext, LateLintPass, Lint};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::DUMMY_SP; use rustc_span::DUMMY_SP;
mod common_metadata;
mod feature_name;
mod multiple_crate_versions;
mod wildcard_dependencies;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks to see if all common metadata is defined in /// Checks to see if all common metadata is defined in

View File

@ -24,7 +24,10 @@ declare_clippy_lint! {
/// Use instead: /// Use instead:
/// ```rust /// ```rust
/// fn is_rust_file(filename: &str) -> bool { /// fn is_rust_file(filename: &str) -> bool {
/// filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true) /// let filename = std::path::Path::new(filename);
/// filename.extension()
/// .map(|ext| ext.eq_ignore_ascii_case("rs"))
/// .unwrap_or(false)
/// } /// }
/// ``` /// ```
#[clippy::version = "1.51.0"] #[clippy::version = "1.51.0"]

View File

@ -1028,9 +1028,10 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| { span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
let sugg = if !snip_is_macro let sugg = if !snip_is_macro
&& expr.precedence().order() < data.position.precedence()
&& !has_enclosing_paren(&snip) && !has_enclosing_paren(&snip)
&& (expr.precedence().order() < data.position.precedence() || calls_field)
{ {
format!("({})", snip) format!("({})", snip)
} else { } else {

View File

@ -9,7 +9,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::kw; use rustc_span::symbol::kw;
use rustc_span::{sym, BytePos, Span}; use rustc_span::{sym, Span};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -85,22 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string, ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string,
_ => false, _ => false,
}; };
let sugg = if format_args.format_string_span.contains(value.span) { let sugg = if is_new_string {
// Implicit argument. e.g. `format!("{x}")` span points to `{x}`
let spdata = value.span.data();
let span = Span::new(
spdata.lo + BytePos(1),
spdata.hi - BytePos(1),
spdata.ctxt,
spdata.parent
);
let snip = snippet_with_applicability(cx, span, "..", &mut applicability);
if is_new_string {
snip.into()
} else {
format!("{snip}.to_string()")
}
} else if is_new_string {
snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned() snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
} else { } else {
let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability); let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);

View File

@ -141,7 +141,7 @@ fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
// Get the hir_id of the object we are calling the method on // Get the hir_id of the object we are calling the method on
if let ExprKind::MethodCall(path, [ref self_arg, ..], _) = expr.kind; if let ExprKind::MethodCall(path, [ref self_arg, ..], _) = expr.kind;
// Is the method to_string() ? // Is the method to_string() ?
if path.ident.name == sym!(to_string); if path.ident.name == sym::to_string;
// Is the method a part of the ToString trait? (i.e. not to_string() implemented // Is the method a part of the ToString trait? (i.e. not to_string() implemented
// separately) // separately)
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);

View File

@ -6,6 +6,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
LintId::of(approx_const::APPROX_CONSTANT), LintId::of(approx_const::APPROX_CONSTANT),
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(attrs::DEPRECATED_CFG_ATTR),
@ -187,6 +188,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEEDLESS_SPLITN),
LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::NO_EFFECT_REPLACE),
LintId::of(methods::OBFUSCATED_IF_ELSE),
LintId::of(methods::OK_EXPECT), LintId::of(methods::OK_EXPECT),
LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_AS_REF_DEREF),
LintId::of(methods::OPTION_FILTER_MAP), LintId::of(methods::OPTION_FILTER_MAP),

View File

@ -42,6 +42,7 @@ store.register_lints(&[
asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
assertions_on_constants::ASSERTIONS_ON_CONSTANTS, assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES,
async_yields_async::ASYNC_YIELDS_ASYNC, async_yields_async::ASYNC_YIELDS_ASYNC,
attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON, attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
@ -330,6 +331,7 @@ store.register_lints(&[
methods::NEEDLESS_SPLITN, methods::NEEDLESS_SPLITN,
methods::NEW_RET_NO_SELF, methods::NEW_RET_NO_SELF,
methods::NO_EFFECT_REPLACE, methods::NO_EFFECT_REPLACE,
methods::OBFUSCATED_IF_ELSE,
methods::OK_EXPECT, methods::OK_EXPECT,
methods::OPTION_AS_REF_DEREF, methods::OPTION_AS_REF_DEREF,
methods::OPTION_FILTER_MAP, methods::OPTION_FILTER_MAP,
@ -417,6 +419,7 @@ store.register_lints(&[
only_used_in_recursion::ONLY_USED_IN_RECURSION, only_used_in_recursion::ONLY_USED_IN_RECURSION,
open_options::NONSENSICAL_OPEN_OPTIONS, open_options::NONSENSICAL_OPEN_OPTIONS,
operators::ABSURD_EXTREME_COMPARISONS, operators::ABSURD_EXTREME_COMPARISONS,
operators::ARITHMETIC,
operators::ASSIGN_OP_PATTERN, operators::ASSIGN_OP_PATTERN,
operators::BAD_BIT_MASK, operators::BAD_BIT_MASK,
operators::CMP_NAN, operators::CMP_NAN,

View File

@ -48,6 +48,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION), LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
LintId::of(module_style::MOD_MODULE_FILES), LintId::of(module_style::MOD_MODULE_FILES),
LintId::of(module_style::SELF_NAMED_MODULE_FILES), LintId::of(module_style::SELF_NAMED_MODULE_FILES),
LintId::of(operators::ARITHMETIC),
LintId::of(operators::FLOAT_ARITHMETIC), LintId::of(operators::FLOAT_ARITHMETIC),
LintId::of(operators::FLOAT_CMP_CONST), LintId::of(operators::FLOAT_CMP_CONST),
LintId::of(operators::INTEGER_ARITHMETIC), LintId::of(operators::INTEGER_ARITHMETIC),

View File

@ -4,6 +4,7 @@
store.register_group(true, "clippy::style", Some("clippy_style"), vec![ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
LintId::of(blacklisted_name::BLACKLISTED_NAME), LintId::of(blacklisted_name::BLACKLISTED_NAME),
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
@ -70,6 +71,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::OBFUSCATED_IF_ELSE),
LintId::of(methods::OK_EXPECT), LintId::of(methods::OK_EXPECT),
LintId::of(methods::OPTION_MAP_OR_NONE), LintId::of(methods::OPTION_MAP_OR_NONE),
LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),

View File

@ -174,6 +174,7 @@ mod as_conversions;
mod as_underscore; mod as_underscore;
mod asm_syntax; mod asm_syntax;
mod assertions_on_constants; mod assertions_on_constants;
mod assertions_on_result_states;
mod async_yields_async; mod async_yields_async;
mod attrs; mod attrs;
mod await_holding_invalid; mod await_holding_invalid;
@ -548,6 +549,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl)); store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
} }
let arithmetic_allowed = conf.arithmetic_allowed.clone();
store.register_late_pass(move || Box::new(operators::arithmetic::Arithmetic::new(arithmetic_allowed.clone())));
store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir));
store.register_late_pass(|| Box::new(utils::author::Author)); store.register_late_pass(|| Box::new(utils::author::Author));
let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
@ -727,6 +730,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy)); store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api))); store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants)); store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates));
store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull)); store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite)); store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
store.register_late_pass(|| Box::new(inherent_to_string::InherentToString)); store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
@ -782,7 +786,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
)) ))
}); });
store.register_late_pass(|| Box::new(default::Default::default())); store.register_late_pass(|| Box::new(default::Default::default()));
store.register_late_pass(|| Box::new(unused_self::UnusedSelf)); store.register_late_pass(move || Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api)));
store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
store.register_late_pass(|| Box::new(exit::Exit)); store.register_late_pass(|| Box::new(exit::Exit));
store.register_late_pass(|| Box::new(to_digit_is_some::ToDigitIsSome)); store.register_late_pass(|| Box::new(to_digit_is_some::ToDigitIsSome));
@ -916,7 +920,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
store.register_late_pass(|| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); store.register_late_pass(|| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports)); store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
// add lints here, do not remove this comment, it's used in `new_lint` // add lints here, do not remove this comment, it's used in `new_lint`
} }

View File

@ -460,7 +460,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
let mut sub_visitor = RefVisitor::new(self.cx); let mut sub_visitor = RefVisitor::new(self.cx);
sub_visitor.visit_fn_decl(decl); sub_visitor.visit_fn_decl(decl);
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
return;
}, },
TyKind::TraitObject(bounds, ref lt, _) => { TyKind::TraitObject(bounds, ref lt, _) => {
if !lt.is_elided() { if !lt.is_elided() {
@ -469,7 +468,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
for bound in bounds { for bound in bounds {
self.visit_poly_trait_ref(bound, TraitBoundModifier::None); self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
} }
return;
}, },
_ => walk_ty(self, ty), _ => walk_ty(self, ty),
} }

View File

@ -1,13 +1,3 @@
use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
use clippy_utils::{higher, in_constant, meets_msrv, msrvs};
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{Span, SpanData, SyntaxContext};
mod collapsible_match; mod collapsible_match;
mod infallible_destructuring_match; mod infallible_destructuring_match;
mod manual_map; mod manual_map;
@ -31,6 +21,16 @@ mod single_match;
mod try_err; mod try_err;
mod wild_in_or_pats; mod wild_in_or_pats;
use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
use clippy_utils::{higher, in_constant, meets_msrv, msrvs};
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{Span, SpanData, SyntaxContext};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for matches with a single arm where an `if let` /// Checks for matches with a single arm where an `if let`
@ -793,18 +793,13 @@ declare_clippy_lint! {
/// ### Example /// ### Example
/// ```rust,ignore /// ```rust,ignore
/// # use std::sync::Mutex; /// # use std::sync::Mutex;
///
/// # struct State {} /// # struct State {}
///
/// # impl State { /// # impl State {
/// # fn foo(&self) -> bool { /// # fn foo(&self) -> bool {
/// # true /// # true
/// # } /// # }
///
/// # fn bar(&self) {} /// # fn bar(&self) {}
/// # } /// # }
///
///
/// let mutex = Mutex::new(State {}); /// let mutex = Mutex::new(State {});
/// ///
/// match mutex.lock().unwrap().foo() { /// match mutex.lock().unwrap().foo() {
@ -815,22 +810,17 @@ declare_clippy_lint! {
/// }; /// };
/// ///
/// println!("All done!"); /// println!("All done!");
///
/// ``` /// ```
/// Use instead: /// Use instead:
/// ```rust /// ```rust
/// # use std::sync::Mutex; /// # use std::sync::Mutex;
///
/// # struct State {} /// # struct State {}
///
/// # impl State { /// # impl State {
/// # fn foo(&self) -> bool { /// # fn foo(&self) -> bool {
/// # true /// # true
/// # } /// # }
///
/// # fn bar(&self) {} /// # fn bar(&self) {}
/// # } /// # }
///
/// let mutex = Mutex::new(State {}); /// let mutex = Mutex::new(State {});
/// ///
/// let is_foo = mutex.lock().unwrap().foo(); /// let is_foo = mutex.lock().unwrap().foo();

View File

@ -14,7 +14,7 @@ use super::INEFFICIENT_TO_STRING;
/// Checks for the `INEFFICIENT_TO_STRING` lint /// Checks for the `INEFFICIENT_TO_STRING` lint
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
if_chain! { if_chain! {
if args.len() == 1 && method_name == sym!(to_string); if args.len() == 1 && method_name == sym::to_string;
if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD); if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id); if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id);

View File

@ -46,6 +46,7 @@ mod map_unwrap_or;
mod needless_option_as_deref; mod needless_option_as_deref;
mod needless_option_take; mod needless_option_take;
mod no_effect_replace; mod no_effect_replace;
mod obfuscated_if_else;
mod ok_expect; mod ok_expect;
mod option_as_ref_deref; mod option_as_ref_deref;
mod option_map_or_none; mod option_map_or_none;
@ -2263,6 +2264,35 @@ declare_clippy_lint! {
"replace with no effect" "replace with no effect"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for usages of `.then_some(..).unwrap_or(..)`
///
/// ### Why is this bad?
/// This can be written more clearly with `if .. else ..`
///
/// ### Limitations
/// This lint currently only looks for usages of
/// `.then_some(..).unwrap_or(..)`, but will be expanded
/// to account for similar patterns.
///
/// ### Example
/// ```rust
/// let x = true;
/// x.then_some("a").unwrap_or("b");
/// ```
/// Use instead:
/// ```rust
/// let x = true;
/// if x { "a" } else { "b" };
/// ```
#[clippy::version = "1.64.0"]
pub OBFUSCATED_IF_ELSE,
style,
"use of `.then_some(..).unwrap_or(..)` can be written \
more clearly with `if .. else ..`"
}
pub struct Methods { pub struct Methods {
avoid_breaking_exported_api: bool, avoid_breaking_exported_api: bool,
msrv: Option<RustcVersion>, msrv: Option<RustcVersion>,
@ -2364,6 +2394,7 @@ impl_lint_pass!(Methods => [
IS_DIGIT_ASCII_RADIX, IS_DIGIT_ASCII_RADIX,
NEEDLESS_OPTION_TAKE, NEEDLESS_OPTION_TAKE,
NO_EFFECT_REPLACE, NO_EFFECT_REPLACE,
OBFUSCATED_IF_ELSE,
]); ]);
/// Extracts a method call name, args, and `Span` of the method name. /// Extracts a method call name, args, and `Span` of the method name.
@ -2772,6 +2803,9 @@ impl Methods {
Some(("map", [m_recv, m_arg], span)) => { Some(("map", [m_recv, m_arg], span)) => {
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
}, },
Some(("then_some", [t_recv, t_arg], _)) => {
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
},
_ => {}, _ => {},
}, },
("unwrap_or_else", [u_arg]) => match method_call(recv) { ("unwrap_or_else", [u_arg]) => match method_call(recv) {

View 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,
);
}
}

View File

@ -18,6 +18,11 @@ declare_clippy_lint! {
/// Naming type parameters inconsistently may cause you to refer to the /// Naming type parameters inconsistently may cause you to refer to the
/// wrong type parameter. /// wrong type parameter.
/// ///
/// ### Limitations
/// This lint only applies to impl blocks with simple generic params, e.g.
/// `A`. If there is anything more complicated, such as a tuple, it will be
/// ignored.
///
/// ### Example /// ### Example
/// ```rust /// ```rust
/// struct Foo<A, B> { /// struct Foo<A, B> {
@ -53,14 +58,15 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
if !generic_args.args.is_empty(); if !generic_args.args.is_empty();
then { then {
// get the name and span of the generic parameters in the Impl // get the name and span of the generic parameters in the Impl
let impl_params = generic_args.args.iter() let mut impl_params = Vec::new();
.filter_map(|p| for p in generic_args.args.iter() {
match p { match p {
GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) => GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
Some((path.segments[0].ident.to_string(), path.span)), impl_params.push((path.segments[0].ident.to_string(), path.span)),
_ => None, GenericArg::Type(_) => return,
} _ => (),
); };
}
// find the type that the Impl is for // find the type that the Impl is for
// only lint on struct/enum/union for now // only lint on struct/enum/union for now
@ -83,8 +89,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect(); type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect();
let type_name = segment.ident; let type_name = segment.ident;
for (i, (impl_param_name, impl_param_span)) in impl_params.enumerate() { for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() {
if mismatch_param_name(i, &impl_param_name, &type_param_names_hashmap) { if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) {
let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order", let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order",
type_name, impl_param_name); type_name, impl_param_name);
let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params", let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params",
@ -92,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
span_lint_and_help( span_lint_and_help(
cx, cx,
MISMATCHING_TYPE_PARAM_ORDER, MISMATCHING_TYPE_PARAM_ORDER,
impl_param_span, *impl_param_span,
&msg, &msg,
None, None,
&help &help

View File

@ -251,14 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
if let ItemKind::Const(hir_ty, body_id) = it.kind { if let ItemKind::Const(hir_ty, body_id) = it.kind {
let ty = hir_ty_to_ty(cx.tcx, hir_ty); let ty = hir_ty_to_ty(cx.tcx, hir_ty);
if !macro_backtrace(it.span).last().map_or(false, |macro_call| { if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
matches!(
cx.tcx.get_diagnostic_name(macro_call.def_id),
Some(sym::thread_local_macro)
)
}) && is_unfrozen(cx, ty)
&& is_value_unfrozen_poly(cx, body_id, ty)
{
lint(cx, Source::Item { item: it.span }); lint(cx, Source::Item { item: it.span });
} }
} }
@ -445,3 +438,12 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
} }
} }
} }
fn ignored_macro(cx: &LateContext<'_>, it: &rustc_hir::Item<'_>) -> bool {
macro_backtrace(it.span).any(|macro_call| {
matches!(
cx.tcx.get_diagnostic_name(macro_call.def_id),
Some(sym::thread_local_macro)
)
})
}

View 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;
}
}
}

View File

@ -8,6 +8,10 @@ use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::mir::FakeReadCause;
use rustc_middle::ty::BorrowKind;
use rustc_trait_selection::infer::TyCtxtInferExt;
use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use super::ASSIGN_OP_PATTERN; use super::ASSIGN_OP_PATTERN;
@ -29,6 +33,16 @@ pub(super) fn check<'tcx>(
.map_or(true, |t| t.path.res.def_id() != trait_id); .map_or(true, |t| t.path.res.def_id() != trait_id);
if implements_trait(cx, ty, trait_id, &[rty.into()]); if implements_trait(cx, ty, trait_id, &[rty.into()]);
then { then {
// Primitive types execute assign-ops right-to-left. Every other type is left-to-right.
if !(ty.is_primitive() && rty.is_primitive()) {
// TODO: This will have false negatives as it doesn't check if the borrows are
// actually live at the end of their respective expressions.
let mut_borrows = mut_borrows_in_expr(cx, assignee);
let imm_borrows = imm_borrows_in_expr(cx, rhs);
if mut_borrows.iter().any(|id| imm_borrows.contains(id)) {
return;
}
}
span_lint_and_then( span_lint_and_then(
cx, cx,
ASSIGN_OP_PATTERN, ASSIGN_OP_PATTERN,
@ -99,3 +113,69 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
walk_expr(self, expr); walk_expr(self, expr);
} }
} }
fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet {
struct S(hir::HirIdSet);
impl Delegate<'_> for S {
fn borrow(&mut self, place: &PlaceWithHirId<'_>, _: hir::HirId, kind: BorrowKind) {
if matches!(kind, BorrowKind::ImmBorrow | BorrowKind::UniqueImmBorrow) {
self.0.insert(match place.place.base {
PlaceBase::Local(id) => id,
PlaceBase::Upvar(id) => id.var_path.hir_id,
_ => return,
});
}
}
fn consume(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {}
fn mutate(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {}
fn fake_read(&mut self, _: &PlaceWithHirId<'_>, _: FakeReadCause, _: hir::HirId) {}
fn copy(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {}
}
let mut s = S(hir::HirIdSet::default());
cx.tcx.infer_ctxt().enter(|infcx| {
let mut v = ExprUseVisitor::new(
&mut s,
&infcx,
cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
cx.param_env,
cx.typeck_results(),
);
v.consume_expr(e);
});
s.0
}
fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet {
struct S(hir::HirIdSet);
impl Delegate<'_> for S {
fn borrow(&mut self, place: &PlaceWithHirId<'_>, _: hir::HirId, kind: BorrowKind) {
if matches!(kind, BorrowKind::MutBorrow) {
self.0.insert(match place.place.base {
PlaceBase::Local(id) => id,
PlaceBase::Upvar(id) => id.var_path.hir_id,
_ => return,
});
}
}
fn consume(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {}
fn mutate(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {}
fn fake_read(&mut self, _: &PlaceWithHirId<'_>, _: FakeReadCause, _: hir::HirId) {}
fn copy(&mut self, _: &PlaceWithHirId<'_>, _: hir::HirId) {}
}
let mut s = S(hir::HirIdSet::default());
cx.tcx.infer_ctxt().enter(|infcx| {
let mut v = ExprUseVisitor::new(
&mut s,
&infcx,
cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
cx.param_env,
cx.typeck_results(),
);
v.consume_expr(e);
});
s.0
}

View File

@ -1,7 +1,3 @@
use rustc_hir::{Body, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
mod absurd_extreme_comparisons; mod absurd_extreme_comparisons;
mod assign_op_pattern; mod assign_op_pattern;
mod bit_mask; mod bit_mask;
@ -25,6 +21,12 @@ mod ptr_eq;
mod self_assignment; mod self_assignment;
mod verbose_bit_mask; mod verbose_bit_mask;
pub(crate) mod arithmetic;
use rustc_hir::{Body, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for comparisons where one side of the relation is /// Checks for comparisons where one side of the relation is
@ -57,6 +59,42 @@ declare_clippy_lint! {
"a comparison with a maximum or minimum value that is always true or false" "a comparison with a maximum or minimum value that is always true or false"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for any kind of arithmetic operation of any type.
///
/// Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing according to the [Rust
/// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
/// or can panic (`/`, `%`). Known safe built-in types like `Wrapping` or `Saturing` are filtered
/// away.
///
/// ### Why is this bad?
/// Integer overflow will trigger a panic in debug builds or will wrap in
/// release mode. Division by zero will cause a panic in either mode. In some applications one
/// wants explicitly checked, wrapping or saturating arithmetic.
///
/// #### Example
/// ```rust
/// # let a = 0;
/// a + 1;
/// ```
///
/// Third-party types also tend to overflow.
///
/// #### Example
/// ```ignore,rust
/// use rust_decimal::Decimal;
/// let _n = Decimal::MAX + Decimal::MAX;
/// ```
///
/// ### Allowed types
/// Custom allowed types can be specified through the "arithmetic-allowed" filter.
#[clippy::version = "1.64.0"]
pub ARITHMETIC,
restriction,
"any arithmetic expression that could overflow or panic"
}
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for integer arithmetic operations which could overflow or panic. /// Checks for integer arithmetic operations which could overflow or panic.
@ -747,6 +785,7 @@ pub struct Operators {
} }
impl_lint_pass!(Operators => [ impl_lint_pass!(Operators => [
ABSURD_EXTREME_COMPARISONS, ABSURD_EXTREME_COMPARISONS,
ARITHMETIC,
INTEGER_ARITHMETIC, INTEGER_ARITHMETIC,
FLOAT_ARITHMETIC, FLOAT_ARITHMETIC,
ASSIGN_OP_PATTERN, ASSIGN_OP_PATTERN,

View File

@ -123,8 +123,8 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr:
if_chain! { if_chain! {
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr); if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
if !is_else_clause(cx.tcx, expr); if !is_else_clause(cx.tcx, expr);
if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind; if let PatKind::TupleStruct(ref path1, [field], None) = let_pat.kind;
if let PatKind::Binding(annot, bind_id, ident, _) = fields[0].kind; if let PatKind::Binding(annot, bind_id, ident, _) = field.kind;
let caller_ty = cx.typeck_results().expr_ty(let_expr); let caller_ty = cx.typeck_results().expr_ty(let_expr);
let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else); let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else);
if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))

View File

@ -1,8 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::{def::Res, HirId, Path, PathSegment}; use rustc_hir::{def::Res, HirId, Path, PathSegment};
use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, symbol::kw, Symbol}; use rustc_span::{sym, symbol::kw, Span};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -63,7 +63,7 @@ declare_clippy_lint! {
/// ### Why is this bad? /// ### Why is this bad?
/// ///
/// Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are /// Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are
/// imported from alloc to ensure disabling `alloc` does not cause the crate to fail to compile. This lint /// imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint
/// is also useful for crates migrating to become `no_std` compatible. /// is also useful for crates migrating to become `no_std` compatible.
/// ///
/// ### Example /// ### Example
@ -81,39 +81,55 @@ declare_clippy_lint! {
"type is imported from alloc when available in core" "type is imported from alloc when available in core"
} }
declare_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]); #[derive(Default)]
pub struct StdReexports {
// Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen
// twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
// when the path could be also be used to access the module.
prev_span: Span,
}
impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
impl<'tcx> LateLintPass<'tcx> for StdReexports { impl<'tcx> LateLintPass<'tcx> for StdReexports {
fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
// std_instead_of_core if let Res::Def(_, def_id) = path.res
check_path(cx, path, sym::std, sym::core, STD_INSTEAD_OF_CORE); && let Some(first_segment) = get_first_segment(path)
// std_instead_of_alloc {
check_path(cx, path, sym::std, sym::alloc, STD_INSTEAD_OF_ALLOC); let (lint, msg, help) = match first_segment.ident.name {
// alloc_instead_of_core sym::std => match cx.tcx.crate_name(def_id.krate) {
check_path(cx, path, sym::alloc, sym::core, ALLOC_INSTEAD_OF_CORE); sym::core => (
} STD_INSTEAD_OF_CORE,
} "used import from `std` instead of `core`",
"consider importing the item from `core`",
fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_crate: Symbol, lint: &'static Lint) { ),
if_chain! { sym::alloc => (
// check if path resolves to the suggested crate. STD_INSTEAD_OF_ALLOC,
if let Res::Def(_, def_id) = path.res; "used import from `std` instead of `alloc`",
if suggested_crate == cx.tcx.crate_name(def_id.krate); "consider importing the item from `alloc`",
),
// check if the first segment of the path is the crate we want to identify _ => {
if let Some(path_root_segment) = get_first_segment(path); self.prev_span = path.span;
return;
// check if the path matches the crate we want to suggest the other path for. },
if krate == path_root_segment.ident.name; },
then { sym::alloc => {
span_lint_and_help( if cx.tcx.crate_name(def_id.krate) == sym::core {
cx, (
lint, ALLOC_INSTEAD_OF_CORE,
path.span, "used import from `alloc` instead of `core`",
&format!("used import from `{}` instead of `{}`", krate, suggested_crate), "consider importing the item from `core`",
None, )
&format!("consider importing the item from `{}`", suggested_crate), } else {
); self.prev_span = path.span;
return;
}
},
_ => return,
};
if path.span != self.prev_span {
span_lint_and_help(cx, lint, path.span, msg, None, help);
self.prev_span = path.span;
}
} }
} }
} }
@ -123,12 +139,10 @@ fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_cr
/// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`] /// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
/// is returned. /// is returned.
fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
let segment = path.segments.first()?; match path.segments {
// A global path will have PathRoot as the first segment. In this case, return the segment after.
// A global path will have PathRoot as the first segment. In this case, return the segment after. [x, y, ..] if x.ident.name == kw::PathRoot => Some(y),
if segment.ident.name == kw::PathRoot { [x, ..] => Some(x),
path.segments.get(1) _ => None,
} else {
Some(segment)
} }
} }

View File

@ -394,7 +394,7 @@ impl<'tcx> LateLintPass<'tcx> for StrToString {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
if_chain! { if_chain! {
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind; if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
if path.ident.name == sym!(to_string); if path.ident.name == sym::to_string;
let ty = cx.typeck_results().expr_ty(self_arg); let ty = cx.typeck_results().expr_ty(self_arg);
if let ty::Ref(_, ty, ..) = ty.kind(); if let ty::Ref(_, ty, ..) = ty.kind();
if *ty.kind() == ty::Str; if *ty.kind() == ty::Str;
@ -444,7 +444,7 @@ impl<'tcx> LateLintPass<'tcx> for StringToString {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
if_chain! { if_chain! {
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind; if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
if path.ident.name == sym!(to_string); if path.ident.name == sym::to_string;
let ty = cx.typeck_results().expr_ty(self_arg); let ty = cx.typeck_results().expr_ty(self_arg);
if is_type_diagnostic_item(cx, ty, sym::String); if is_type_diagnostic_item(cx, ty, sym::String);
then { then {

View File

@ -3,7 +3,7 @@ use clippy_utils::visitors::is_local_used;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -35,7 +35,19 @@ declare_clippy_lint! {
"methods that contain a `self` argument but don't use it" "methods that contain a `self` argument but don't use it"
} }
declare_lint_pass!(UnusedSelf => [UNUSED_SELF]); pub struct UnusedSelf {
avoid_breaking_exported_api: bool,
}
impl_lint_pass!(UnusedSelf => [UNUSED_SELF]);
impl UnusedSelf {
pub fn new(avoid_breaking_exported_api: bool) -> Self {
Self {
avoid_breaking_exported_api,
}
}
}
impl<'tcx> LateLintPass<'tcx> for UnusedSelf { impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'_>) { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'_>) {
@ -49,6 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind; if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind;
if assoc_item.fn_has_self_parameter; if assoc_item.fn_has_self_parameter;
if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
if !cx.access_levels.is_exported(impl_item.def_id) || !self.avoid_breaking_exported_api;
let body = cx.tcx.hir().body(*body_id); let body = cx.tcx.hir().body(*body_id);
if let [self_param, ..] = body.params; if let [self_param, ..] = body.params;
if !is_local_used(cx, body, self_param.pat.hir_id); if !is_local_used(cx, body, self_param.pat.hir_id);

View File

@ -191,7 +191,11 @@ macro_rules! define_Conf {
} }
define_Conf! { define_Conf! {
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX. /// Lint: Arithmetic.
///
/// Suppress checking of the passed type names.
(arithmetic_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
/// ///
/// Suppress lints whenever the suggested change would cause breakage for other crates. /// Suppress lints whenever the suggested change would cause breakage for other crates.
(avoid_breaking_exported_api: bool = true), (avoid_breaking_exported_api: bool = true),

View File

@ -619,32 +619,24 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -
}, },
mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() { mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() {
ty::Array(sub_type, len) => match sub_type.kind() { ty::Array(sub_type, len) => match sub_type.kind() {
ty::Float(FloatTy::F32) => match len.to_valtree().try_to_machine_usize(tcx) { ty::Float(FloatTy::F32) => match len.kind().try_to_machine_usize(tcx) {
Some(len) => alloc Some(len) => alloc
.inner() .inner()
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap())) .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
.to_owned() .to_owned()
.chunks(4) .array_chunks::<4>()
.map(|chunk| { .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
Some(Constant::F32(f32::from_le_bytes(
chunk.try_into().expect("this shouldn't happen"),
)))
})
.collect::<Option<Vec<Constant>>>() .collect::<Option<Vec<Constant>>>()
.map(Constant::Vec), .map(Constant::Vec),
_ => None, _ => None,
}, },
ty::Float(FloatTy::F64) => match len.to_valtree().try_to_machine_usize(tcx) { ty::Float(FloatTy::F64) => match len.kind().try_to_machine_usize(tcx) {
Some(len) => alloc Some(len) => alloc
.inner() .inner()
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap())) .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
.to_owned() .to_owned()
.chunks(8) .array_chunks::<8>()
.map(|chunk| { .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
Some(Constant::F64(f64::from_le_bytes(
chunk.try_into().expect("this shouldn't happen"),
)))
})
.collect::<Option<Vec<Constant>>>() .collect::<Option<Vec<Constant>>>()
.map(Constant::Vec), .map(Constant::Vec),
_ => None, _ => None,

View File

@ -127,9 +127,6 @@ impl HirEqInterExpr<'_, '_, '_> {
/// Checks whether two blocks are the same. /// Checks whether two blocks are the same.
fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
if self.cannot_be_compared_block(left) || self.cannot_be_compared_block(right) {
return false;
}
match (left.stmts, left.expr, right.stmts, right.expr) { match (left.stmts, left.expr, right.stmts, right.expr) {
([], None, [], None) => { ([], None, [], None) => {
// For empty blocks, check to see if the tokens are equal. This will catch the case where a macro // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
@ -180,36 +177,13 @@ impl HirEqInterExpr<'_, '_, '_> {
} }
} }
fn cannot_be_compared_block(&mut self, block: &Block<'_>) -> bool {
if block.stmts.last().map_or(false, |stmt| {
matches!(
stmt.kind,
StmtKind::Semi(semi_expr) if self.should_ignore(semi_expr)
)
}) {
return true;
}
if let Some(block_expr) = block.expr
&& self.should_ignore(block_expr)
{
return true
}
false
}
fn should_ignore(&mut self, expr: &Expr<'_>) -> bool { fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
if macro_backtrace(expr.span).last().map_or(false, |macro_call| { macro_backtrace(expr.span).last().map_or(false, |macro_call| {
matches!( matches!(
&self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id), &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
Some(sym::todo_macro | sym::unimplemented_macro) Some(sym::todo_macro | sym::unimplemented_macro)
) )
}) { })
return true;
}
false
} }
pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool { pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool {
@ -327,7 +301,8 @@ impl HirEqInterExpr<'_, '_, '_> {
(&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re), (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
_ => false, _ => false,
}; };
is_eq || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right)) (is_eq && (!self.should_ignore(left) || !self.should_ignore(right)))
|| self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
} }
fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool { fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {

View File

@ -1,3 +1,4 @@
#![feature(array_chunks)]
#![feature(box_patterns)] #![feature(box_patterns)]
#![feature(control_flow_enum)] #![feature(control_flow_enum)]
#![feature(let_else)] #![feature(let_else)]
@ -2141,7 +2142,7 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new(); static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default())); let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap(); let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
let value = map.entry(module); let value = map.entry(module);

View File

@ -1,3 +1,3 @@
[toolchain] [toolchain]
channel = "nightly-2022-07-15" channel = "nightly-2022-07-28"
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]

View File

@ -433,7 +433,7 @@ fn rustfix_coverage_known_exceptions_accuracy() {
let rs_path = Path::new("tests/ui").join(filename); let rs_path = Path::new("tests/ui").join(filename);
assert!( assert!(
rs_path.exists(), rs_path.exists(),
"`{}` does not exists", "`{}` does not exist",
rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display() rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
); );
let fixed_path = rs_path.with_extension("fixed"); let fixed_path = rs_path.with_extension("fixed");
@ -445,6 +445,45 @@ fn rustfix_coverage_known_exceptions_accuracy() {
} }
} }
#[test]
fn ui_cargo_toml_metadata() {
let ui_cargo_path = Path::new("tests/ui-cargo");
let cargo_common_metadata_path = ui_cargo_path.join("cargo_common_metadata");
let publish_exceptions =
["fail_publish", "fail_publish_true", "pass_publish_empty"].map(|path| cargo_common_metadata_path.join(path));
for entry in walkdir::WalkDir::new(ui_cargo_path) {
let entry = entry.unwrap();
let path = entry.path();
if path.file_name() != Some(OsStr::new("Cargo.toml")) {
continue;
}
let toml = fs::read_to_string(path).unwrap().parse::<toml::Value>().unwrap();
let package = toml.as_table().unwrap().get("package").unwrap().as_table().unwrap();
let name = package.get("name").unwrap().as_str().unwrap().replace('-', "_");
assert!(
path.parent()
.unwrap()
.components()
.map(|component| component.as_os_str().to_string_lossy().replace('-', "_"))
.any(|s| *s == name)
|| path.starts_with(&cargo_common_metadata_path),
"{:?} has incorrect package name",
path
);
let publish = package.get("publish").and_then(toml::Value::as_bool).unwrap_or(true);
assert!(
!publish || publish_exceptions.contains(&path.parent().unwrap().to_path_buf()),
"{:?} lacks `publish = false`",
path
);
}
}
/// Restores an env var on drop /// Restores an env var on drop
#[must_use] #[must_use]
struct VarGuard { struct VarGuard {

View File

@ -1,5 +1,5 @@
[package] [package]
name = "cargo_common_metadata" name = "cargo_common_metadata_fail"
version = "0.1.0" version = "0.1.0"
publish = false publish = false

View File

@ -1,16 +1,16 @@
error: package `cargo_common_metadata` is missing `package.description` metadata error: package `cargo_common_metadata_fail` is missing `package.description` metadata
| |
= note: `-D clippy::cargo-common-metadata` implied by `-D warnings` = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata error: package `cargo_common_metadata_fail` is missing `either package.license or package.license_file` metadata
error: package `cargo_common_metadata` is missing `package.repository` metadata error: package `cargo_common_metadata_fail` is missing `package.repository` metadata
error: package `cargo_common_metadata` is missing `package.readme` metadata error: package `cargo_common_metadata_fail` is missing `package.readme` metadata
error: package `cargo_common_metadata` is missing `package.keywords` metadata error: package `cargo_common_metadata_fail` is missing `package.keywords` metadata
error: package `cargo_common_metadata` is missing `package.categories` metadata error: package `cargo_common_metadata_fail` is missing `package.categories` metadata
error: aborting due to 6 previous errors error: aborting due to 6 previous errors

View File

@ -1,5 +1,5 @@
[package] [package]
name = "cargo_common_metadata" name = "cargo_common_metadata_fail_publish"
version = "0.1.0" version = "0.1.0"
publish = ["some-registry-name"] publish = ["some-registry-name"]

View File

@ -1,16 +1,16 @@
error: package `cargo_common_metadata` is missing `package.description` metadata error: package `cargo_common_metadata_fail_publish` is missing `package.description` metadata
| |
= note: `-D clippy::cargo-common-metadata` implied by `-D warnings` = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata error: package `cargo_common_metadata_fail_publish` is missing `either package.license or package.license_file` metadata
error: package `cargo_common_metadata` is missing `package.repository` metadata error: package `cargo_common_metadata_fail_publish` is missing `package.repository` metadata
error: package `cargo_common_metadata` is missing `package.readme` metadata error: package `cargo_common_metadata_fail_publish` is missing `package.readme` metadata
error: package `cargo_common_metadata` is missing `package.keywords` metadata error: package `cargo_common_metadata_fail_publish` is missing `package.keywords` metadata
error: package `cargo_common_metadata` is missing `package.categories` metadata error: package `cargo_common_metadata_fail_publish` is missing `package.categories` metadata
error: aborting due to 6 previous errors error: aborting due to 6 previous errors

View File

@ -1,5 +1,5 @@
[package] [package]
name = "cargo_common_metadata" name = "cargo_common_metadata_fail_publish_true"
version = "0.1.0" version = "0.1.0"
publish = true publish = true

View File

@ -1,16 +1,16 @@
error: package `cargo_common_metadata` is missing `package.description` metadata error: package `cargo_common_metadata_fail_publish_true` is missing `package.description` metadata
| |
= note: `-D clippy::cargo-common-metadata` implied by `-D warnings` = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata error: package `cargo_common_metadata_fail_publish_true` is missing `either package.license or package.license_file` metadata
error: package `cargo_common_metadata` is missing `package.repository` metadata error: package `cargo_common_metadata_fail_publish_true` is missing `package.repository` metadata
error: package `cargo_common_metadata` is missing `package.readme` metadata error: package `cargo_common_metadata_fail_publish_true` is missing `package.readme` metadata
error: package `cargo_common_metadata` is missing `package.keywords` metadata error: package `cargo_common_metadata_fail_publish_true` is missing `package.keywords` metadata
error: package `cargo_common_metadata` is missing `package.categories` metadata error: package `cargo_common_metadata_fail_publish_true` is missing `package.categories` metadata
error: aborting due to 6 previous errors error: aborting due to 6 previous errors

View File

@ -1,5 +1,5 @@
[package] [package]
name = "cargo_common_metadata" name = "cargo_common_metadata_pass"
version = "0.1.0" version = "0.1.0"
publish = false publish = false
description = "A test package for the cargo_common_metadata lint" description = "A test package for the cargo_common_metadata lint"

View File

@ -1,5 +1,5 @@
[package] [package]
name = "cargo_common_metadata" name = "cargo_common_metadata_pass_publish_empty"
version = "0.1.0" version = "0.1.0"
publish = [] publish = []

View File

@ -1,5 +1,5 @@
[package] [package]
name = "cargo_common_metadata" name = "cargo_common_metadata_pass_publish_false"
version = "0.1.0" version = "0.1.0"
publish = false publish = false

View File

@ -2,6 +2,7 @@
name = "fail-both-diff" name = "fail-both-diff"
version = "0.1.0" version = "0.1.0"
rust-version = "1.56" rust-version = "1.56"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -2,6 +2,7 @@
name = "fail-both-same" name = "fail-both-same"
version = "0.1.0" version = "0.1.0"
rust-version = "1.57.0" rust-version = "1.57.0"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -2,6 +2,7 @@
name = "fail-cargo" name = "fail-cargo"
version = "0.1.0" version = "0.1.0"
rust-version = "1.56.1" rust-version = "1.56.1"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,6 +1,7 @@
[package] [package]
name = "fail-clippy" name = "fail-clippy"
version = "0.1.0" version = "0.1.0"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -2,6 +2,7 @@
name = "fail-file-attr" name = "fail-file-attr"
version = "0.1.0" version = "0.1.0"
rust-version = "1.13" rust-version = "1.13"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,7 +1,8 @@
[package] [package]
name = "fail-both-same" name = "pass-both-same"
version = "0.1.0" version = "0.1.0"
rust-version = "1.13.0" rust-version = "1.13.0"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,7 +1,8 @@
[package] [package]
name = "fail-cargo" name = "pass-cargo"
version = "0.1.0" version = "0.1.0"
rust-version = "1.13.0" rust-version = "1.13.0"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,6 +1,7 @@
[package] [package]
name = "fail-clippy" name = "pass-clippy"
version = "0.1.0" version = "0.1.0"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,7 +1,8 @@
[package] [package]
name = "fail-file-attr" name = "pass-file-attr"
version = "0.1.0" version = "0.1.0"
rust-version = "1.59" rust-version = "1.59"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -2,6 +2,7 @@
name = "warn-both-diff" name = "warn-both-diff"
version = "0.1.0" version = "0.1.0"
rust-version = "1.56.0" rust-version = "1.56.0"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,7 +1,8 @@
[package] [package]
name = "fail" name = "fail-mod"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2018"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,7 +1,8 @@
[package] [package]
name = "fail" name = "fail-no-mod"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2018"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,7 +1,8 @@
[package] [package]
name = "fail" name = "pass-mod"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2018"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,7 +1,8 @@
[package] [package]
name = "pass" name = "pass-no-mod"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2018"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -2,6 +2,7 @@
name = "no_warn" name = "no_warn"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -2,6 +2,7 @@
name = "warn" name = "warn"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,5 +1,3 @@
// ignore-windows
fn main() { fn main() {
println!("Hello, world!"); println!("Hello, world!");
} }

View File

@ -1,5 +1,5 @@
[package] [package]
name = "cargo_common_metadata" name = "multiple_crate_versions"
version = "0.1.0" version = "0.1.0"
publish = false publish = false

View File

@ -17,7 +17,7 @@ LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
= note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
= help: please use a valid sematic version, see `doc/adding_lints.md` = help: please use a valid sematic version, see `doc/adding_lints.md`
= note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
error: this item has an invalid `clippy::version` attribute error: this item has an invalid `clippy::version` attribute
--> $DIR/check_clippy_version_attribute.rs:48:1 --> $DIR/check_clippy_version_attribute.rs:48:1
@ -32,7 +32,7 @@ LL | | }
| |_^ | |_^
| |
= help: please use a valid sematic version, see `doc/adding_lints.md` = help: please use a valid sematic version, see `doc/adding_lints.md`
= note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
error: this lint is missing the `clippy::version` attribute or version value error: this lint is missing the `clippy::version` attribute or version value
--> $DIR/check_clippy_version_attribute.rs:59:1 --> $DIR/check_clippy_version_attribute.rs:59:1
@ -48,7 +48,7 @@ LL | | }
| |
= note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
= help: please use a `clippy::version` attribute, see `doc/adding_lints.md` = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
= note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
error: this lint is missing the `clippy::version` attribute or version value error: this lint is missing the `clippy::version` attribute or version value
--> $DIR/check_clippy_version_attribute.rs:67:1 --> $DIR/check_clippy_version_attribute.rs:67:1
@ -62,7 +62,7 @@ LL | | }
| |_^ | |_^
| |
= help: please use a `clippy::version` attribute, see `doc/adding_lints.md` = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
= note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 4 previous errors error: aborting due to 4 previous errors

View File

@ -15,7 +15,7 @@ note: the lint level is defined here
LL | #![deny(clippy::internal)] LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
= note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]`
= note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error error: aborting due to previous error

View File

@ -56,7 +56,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
= note: this error originates in the macro `__if_chain` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `__if_chain` which comes from the expansion of the macro `if_chain` (in Nightly builds, run with -Z macro-backtrace for more info)
error: `let` expression should be above the `if_chain!` error: `let` expression should be above the `if_chain!`
--> $DIR/if_chain_style.rs:40:9 --> $DIR/if_chain_style.rs:40:9

View 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;
}

View File

@ -0,0 +1 @@
arithmetic-allowed = ["Point"]

View File

@ -3,6 +3,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie
allow-expect-in-tests allow-expect-in-tests
allow-unwrap-in-tests allow-unwrap-in-tests
allowed-scripts allowed-scripts
arithmetic-allowed
array-size-threshold array-size-threshold
avoid-breaking-exported-api avoid-breaking-exported-api
await-holding-invalid-types await-holding-invalid-types

View 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() {}

View 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() {}

View 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());
}

View 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());
}

View 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

View File

@ -1,5 +1,7 @@
// run-rustfix // run-rustfix
use core::num::Wrapping;
#[allow(dead_code, unused_assignments)] #[allow(dead_code, unused_assignments)]
#[warn(clippy::assign_op_pattern)] #[warn(clippy::assign_op_pattern)]
fn main() { fn main() {
@ -18,4 +20,13 @@ fn main() {
a = 6 << a; a = 6 << a;
let mut s = String::new(); let mut s = String::new();
s += "bla"; s += "bla";
// Issue #9180
let mut a = Wrapping(0u32);
a += Wrapping(1u32);
let mut v = vec![0u32, 1u32];
v[0] += v[1];
let mut v = vec![Wrapping(0u32), Wrapping(1u32)];
v[0] = v[0] + v[1];
let _ = || v[0] = v[0] + v[1];
} }

View File

@ -1,5 +1,7 @@
// run-rustfix // run-rustfix
use core::num::Wrapping;
#[allow(dead_code, unused_assignments)] #[allow(dead_code, unused_assignments)]
#[warn(clippy::assign_op_pattern)] #[warn(clippy::assign_op_pattern)]
fn main() { fn main() {
@ -18,4 +20,13 @@ fn main() {
a = 6 << a; a = 6 << a;
let mut s = String::new(); let mut s = String::new();
s = s + "bla"; s = s + "bla";
// Issue #9180
let mut a = Wrapping(0u32);
a = a + Wrapping(1u32);
let mut v = vec![0u32, 1u32];
v[0] = v[0] + v[1];
let mut v = vec![Wrapping(0u32), Wrapping(1u32)];
v[0] = v[0] + v[1];
let _ = || v[0] = v[0] + v[1];
} }

View File

@ -1,5 +1,5 @@
error: manual implementation of an assign operation error: manual implementation of an assign operation
--> $DIR/assign_ops.rs:7:5 --> $DIR/assign_ops.rs:9:5
| |
LL | a = a + 1; LL | a = a + 1;
| ^^^^^^^^^ help: replace it with: `a += 1` | ^^^^^^^^^ help: replace it with: `a += 1`
@ -7,52 +7,64 @@ LL | a = a + 1;
= note: `-D clippy::assign-op-pattern` implied by `-D warnings` = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
error: manual implementation of an assign operation error: manual implementation of an assign operation
--> $DIR/assign_ops.rs:8:5 --> $DIR/assign_ops.rs:10:5
| |
LL | a = 1 + a; LL | a = 1 + a;
| ^^^^^^^^^ help: replace it with: `a += 1` | ^^^^^^^^^ help: replace it with: `a += 1`
error: manual implementation of an assign operation error: manual implementation of an assign operation
--> $DIR/assign_ops.rs:9:5 --> $DIR/assign_ops.rs:11:5
| |
LL | a = a - 1; LL | a = a - 1;
| ^^^^^^^^^ help: replace it with: `a -= 1` | ^^^^^^^^^ help: replace it with: `a -= 1`
error: manual implementation of an assign operation error: manual implementation of an assign operation
--> $DIR/assign_ops.rs:10:5 --> $DIR/assign_ops.rs:12:5
| |
LL | a = a * 99; LL | a = a * 99;
| ^^^^^^^^^^ help: replace it with: `a *= 99` | ^^^^^^^^^^ help: replace it with: `a *= 99`
error: manual implementation of an assign operation error: manual implementation of an assign operation
--> $DIR/assign_ops.rs:11:5 --> $DIR/assign_ops.rs:13:5
| |
LL | a = 42 * a; LL | a = 42 * a;
| ^^^^^^^^^^ help: replace it with: `a *= 42` | ^^^^^^^^^^ help: replace it with: `a *= 42`
error: manual implementation of an assign operation error: manual implementation of an assign operation
--> $DIR/assign_ops.rs:12:5 --> $DIR/assign_ops.rs:14:5
| |
LL | a = a / 2; LL | a = a / 2;
| ^^^^^^^^^ help: replace it with: `a /= 2` | ^^^^^^^^^ help: replace it with: `a /= 2`
error: manual implementation of an assign operation error: manual implementation of an assign operation
--> $DIR/assign_ops.rs:13:5 --> $DIR/assign_ops.rs:15:5
| |
LL | a = a % 5; LL | a = a % 5;
| ^^^^^^^^^ help: replace it with: `a %= 5` | ^^^^^^^^^ help: replace it with: `a %= 5`
error: manual implementation of an assign operation error: manual implementation of an assign operation
--> $DIR/assign_ops.rs:14:5 --> $DIR/assign_ops.rs:16:5
| |
LL | a = a & 1; LL | a = a & 1;
| ^^^^^^^^^ help: replace it with: `a &= 1` | ^^^^^^^^^ help: replace it with: `a &= 1`
error: manual implementation of an assign operation error: manual implementation of an assign operation
--> $DIR/assign_ops.rs:20:5 --> $DIR/assign_ops.rs:22:5
| |
LL | s = s + "bla"; LL | s = s + "bla";
| ^^^^^^^^^^^^^ help: replace it with: `s += "bla"` | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"`
error: aborting due to 9 previous errors error: manual implementation of an assign operation
--> $DIR/assign_ops.rs:26:5
|
LL | a = a + Wrapping(1u32);
| ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)`
error: manual implementation of an assign operation
--> $DIR/assign_ops.rs:28:5
|
LL | v[0] = v[0] + v[1];
| ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]`
error: aborting due to 11 previous errors

View 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] };
}

View File

@ -0,0 +1,8 @@
enum E {
X(),
Y,
}
fn main() {
let _ = if let E::X() = E::X() { 1 } else { 2 };
}

View File

@ -1,5 +1,4 @@
// ignore-macos // ignore-macos
// ignore-windows
#![feature(rustc_attrs)] #![feature(rustc_attrs)]

View File

@ -1,5 +1,5 @@
error: recursing into entrypoint `a` error: recursing into entrypoint `a`
--> $DIR/entrypoint_recursion.rs:11:5 --> $DIR/entrypoint_recursion.rs:10:5
| |
LL | a(); LL | a();
| ^ | ^

View File

@ -31,9 +31,25 @@ const NO_ANN: &dyn Display = &70;
static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
//^ there should be no lints on this line //^ there should be no lints on this line
// issue #8493 mod issue_8493 {
thread_local! { use std::cell::Cell;
static THREAD_LOCAL: Cell<i32> = const { Cell::new(0) };
thread_local! {
static _BAR: Cell<i32> = const { Cell::new(0) };
}
macro_rules! issue_8493 {
() => {
const _BAZ: Cell<usize> = Cell::new(0); //~ ERROR interior mutable
static _FOOBAR: () = {
thread_local! {
static _VAR: Cell<i32> = const { Cell::new(0) };
}
};
};
}
issue_8493!();
} }
fn main() {} fn main() {}

View File

@ -35,5 +35,16 @@ LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
| |
= note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 4 previous errors error: a `const` item should never be interior mutable
--> $DIR/others.rs:43:13
|
LL | const _BAZ: Cell<usize> = Cell::new(0); //~ ERROR interior mutable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | issue_8493!();
| ------------- in this macro invocation
|
= note: this error originates in the macro `issue_8493` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 5 previous errors

View File

@ -84,4 +84,10 @@ fn main() {
let _ = x.to_string(); let _ = x.to_string();
let _ = format!("{x:?}"); // Don't lint on debug let _ = format!("{x:?}"); // Don't lint on debug
let _ = x.to_string(); let _ = x.to_string();
// Issue #9234
let abc = "abc";
let _ = abc.to_string();
let xx = "xx";
let _ = xx.to_string();
} }

View File

@ -86,4 +86,10 @@ fn main() {
let _ = format!("{x}"); let _ = format!("{x}");
let _ = format!("{x:?}"); // Don't lint on debug let _ = format!("{x:?}"); // Don't lint on debug
let _ = format!("{y}", y = x); let _ = format!("{y}", y = x);
// Issue #9234
let abc = "abc";
let _ = format!("{abc}");
let xx = "xx";
let _ = format!("{xx}");
} }

View File

@ -111,5 +111,17 @@ error: useless use of `format!`
LL | let _ = format!("{y}", y = x); LL | let _ = format!("{y}", y = x);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: aborting due to 17 previous errors error: useless use of `format!`
--> $DIR/format.rs:92:13
|
LL | let _ = format!("{abc}");
| ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:94:13
|
LL | let _ = format!("{xx}");
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`
error: aborting due to 19 previous errors

View File

@ -1,5 +1,5 @@
#![warn(clippy::match_same_arms)] #![warn(clippy::match_same_arms)]
#![allow(clippy::blacklisted_name)] #![allow(clippy::blacklisted_name, clippy::diverging_sub_expression)]
fn bar<T>(_: T) {} fn bar<T>(_: T) {}
fn foo() -> bool { fn foo() -> bool {
@ -227,4 +227,12 @@ fn main() {
Some(Bar { y: 0, x: 5, .. }) => 1, Some(Bar { y: 0, x: 5, .. }) => 1,
_ => 200, _ => 200,
}; };
let _ = match 0 {
0 => todo!(),
1 => todo!(),
2 => core::convert::identity::<u32>(todo!()),
3 => core::convert::identity::<u32>(todo!()),
_ => 5,
};
} }

View File

@ -57,4 +57,8 @@ fn main() {
B: Copy, B: Copy,
{ {
} }
// if the types are complicated, do not lint
impl<K, V, B> Foo<(K, V), B> {}
impl<K, V, A> Foo<(K, V), A> {}
} }

View File

@ -158,3 +158,28 @@ fn check_expect_suppression() {
#[expect(clippy::needless_borrow)] #[expect(clippy::needless_borrow)]
let _ = x(&&a); let _ = x(&&a);
} }
#[allow(dead_code)]
mod issue9160 {
pub struct S<F> {
f: F,
}
impl<T, F> S<F>
where
F: Fn() -> T,
{
fn calls_field(&self) -> T {
(self.f)()
}
}
impl<T, F> S<F>
where
F: FnMut() -> T,
{
fn calls_mut_field(&mut self) -> T {
(self.f)()
}
}
}

View File

@ -158,3 +158,28 @@ fn check_expect_suppression() {
#[expect(clippy::needless_borrow)] #[expect(clippy::needless_borrow)]
let _ = x(&&a); let _ = x(&&a);
} }
#[allow(dead_code)]
mod issue9160 {
pub struct S<F> {
f: F,
}
impl<T, F> S<F>
where
F: Fn() -> T,
{
fn calls_field(&self) -> T {
(&self.f)()
}
}
impl<T, F> S<F>
where
F: FnMut() -> T,
{
fn calls_mut_field(&mut self) -> T {
(&mut self.f)()
}
}
}

View File

@ -120,5 +120,17 @@ error: this expression creates a reference which is immediately dereferenced by
LL | (&&5).foo(); LL | (&&5).foo();
| ^^^^^ help: change this to: `(&5)` | ^^^^^ help: change this to: `(&5)`
error: aborting due to 20 previous errors error: this expression borrows a value the compiler would automatically borrow
--> $DIR/needless_borrow.rs:173:13
|
LL | (&self.f)()
| ^^^^^^^^^ help: change this to: `(self.f)`
error: this expression borrows a value the compiler would automatically borrow
--> $DIR/needless_borrow.rs:182:13
|
LL | (&mut self.f)()
| ^^^^^^^^^^^^^ help: change this to: `(self.f)`
error: aborting due to 22 previous errors

View File

@ -0,0 +1,7 @@
// run-rustfix
#![warn(clippy::obfuscated_if_else)]
fn main() {
if true { "a" } else { "b" };
}

View File

@ -0,0 +1,7 @@
// run-rustfix
#![warn(clippy::obfuscated_if_else)]
fn main() {
true.then_some("a").unwrap_or("b");
}

View 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

View File

@ -9,6 +9,8 @@ fn std_instead_of_core() {
use std::hash::Hasher; use std::hash::Hasher;
// Absolute path // Absolute path
use ::std::hash::Hash; use ::std::hash::Hash;
// Don't lint on `env` macro
use std::env;
// Multiple imports // Multiple imports
use std::fmt::{Debug, Result}; use std::fmt::{Debug, Result};
@ -20,10 +22,14 @@ fn std_instead_of_core() {
// Types // Types
let cell = std::cell::Cell::new(8u32); let cell = std::cell::Cell::new(8u32);
let cell_absolute = ::std::cell::Cell::new(8u32); let cell_absolute = ::std::cell::Cell::new(8u32);
let _ = std::env!("PATH");
} }
#[warn(clippy::std_instead_of_alloc)] #[warn(clippy::std_instead_of_alloc)]
fn std_instead_of_alloc() { fn std_instead_of_alloc() {
// Only lint once.
use std::vec;
use std::vec::Vec; use std::vec::Vec;
} }

Some files were not shown because too many files have changed in this diff Show More