Merge remote-tracking branch 'upstream/master' into rustup

This commit is contained in:
Philipp Krones 2022-07-28 18:47:48 +02:00
commit 0905ec465d
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
117 changed files with 1579 additions and 346 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_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
[`arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
[`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states
[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
@ -3793,6 +3795,7 @@ Released 2018-09-13
[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
[`obfuscated_if_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#obfuscated_if_else
[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion

View file

@ -32,6 +32,7 @@ compiletest_rs = { version = "0.8", features = ["tmp"] }
tester = "0.9"
regex = "1.5"
toml = "0.5"
walkdir = "2.3"
# This is used by the `collect-metadata` alias.
filetime = "0.2"
@ -41,6 +42,7 @@ filetime = "0.2"
rustc-workspace-hack = "1.0"
# UI test dependencies
clap = { version = "3.1", features = ["derive"] }
clippy_utils = { path = "clippy_utils" }
derive-new = "0.5"
if_chain = "1.0"

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,3 @@
#![cfg_attr(bootstrap, feature(let_chains))]
#![feature(let_else)]
#![feature(once_cell)]
#![feature(rustc_private)]

View file

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

View file

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

View file

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

View file

@ -18,7 +18,7 @@ quine-mc_cluskey = "0.2"
regex-syntax = "0.6"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", optional = true }
tempfile = { version = "3.2", optional = true }
tempfile = { version = "3.3.0", optional = true }
toml = "0.5"
unicode-normalization = "0.1"
unicode-script = { version = "0.5", default-features = false }

View file

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

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 clippy_utils::diagnostics::span_lint;
use clippy_utils::is_lint_allowed;
@ -6,11 +11,6 @@ use rustc_lint::{LateContext, LateLintPass, Lint};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::DUMMY_SP;
mod common_metadata;
mod feature_name;
mod multiple_crate_versions;
mod wildcard_dependencies;
declare_clippy_lint! {
/// ### What it does
/// Checks to see if all common metadata is defined in

View file

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

View file

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

View file

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

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
if let ExprKind::MethodCall(path, [ref self_arg, ..], _) = expr.kind;
// Is the method to_string() ?
if path.ident.name == sym!(to_string);
if path.ident.name == sym::to_string;
// Is the method a part of the ToString trait? (i.e. not to_string() implemented
// separately)
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);

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

View file

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

View file

@ -28,6 +28,8 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
LintId::of(strings::STRING_LIT_AS_BYTES),
LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
LintId::of(unused_rounding::UNUSED_ROUNDING),
LintId::of(use_self::USE_SELF),

View file

@ -86,8 +86,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(strings::STRING_ADD_ASSIGN),
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
LintId::of(types::LINKEDLIST),
LintId::of(types::OPTION_OPTION),

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(module_style::MOD_MODULE_FILES),
LintId::of(module_style::SELF_NAMED_MODULE_FILES),
LintId::of(operators::ARITHMETIC),
LintId::of(operators::FLOAT_ARITHMETIC),
LintId::of(operators::FLOAT_CMP_CONST),
LintId::of(operators::INTEGER_ARITHMETIC),

View file

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

View file

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

View file

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

View file

@ -11,7 +11,7 @@ use rustc_lint::LateContext;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) {
([stmt, stmts @ ..], expr) => {
if let StmtKind::Local(&Local { init: Some(e), els: None, .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind {
if let StmtKind::Local(&Local { init: Some(e), .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind {
(e, !stmts.is_empty() || expr.is_some())
} else {
return;

View file

@ -35,7 +35,8 @@ struct PathAndSpan {
span: Span,
}
/// `MacroRefData` includes the name of the macro.
/// `MacroRefData` includes the name of the macro
/// and the path from `SourceMap::span_to_filename`.
#[derive(Debug, Clone)]
pub struct MacroRefData {
name: String,

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

View file

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

View file

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

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

View file

@ -7,7 +7,8 @@
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast;
use if_chain::if_chain;
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::{self, DefIdTree};
@ -57,6 +58,20 @@ impl MissingDoc {
*self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
}
fn has_include(meta: Option<MetaItem>) -> bool {
if_chain! {
if let Some(meta) = meta;
if let MetaItemKind::List(list) = meta.kind;
if let Some(meta) = list.get(0);
if let Some(name) = meta.ident();
then {
name.name == sym::include
} else {
false
}
}
}
fn check_missing_docs_attrs(
&self,
cx: &LateContext<'_>,
@ -80,7 +95,9 @@ impl MissingDoc {
return;
}
let has_doc = attrs.iter().any(|a| a.doc_str().is_some());
let has_doc = attrs
.iter()
.any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
if !has_doc {
span_lint(
cx,

View file

@ -95,10 +95,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
self.found = true;
return;
},
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj

View file

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

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

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

View file

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

View file

@ -396,7 +396,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args:
// `.iter()` and `.len()` called on same `Path`
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind;
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
then {
span_lint(cx,
RANGE_ZIP_WITH_LEN,

View file

@ -55,11 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
if matches!(cx.tcx.def_kind(id.def_id), DefKind::Impl)
&& let item = cx.tcx.hir().item(id)
&& let ItemKind::Impl(Impl {
items,
of_trait,
self_ty,
..
}) = &item.kind
items,
of_trait,
self_ty,
..
}) = &item.kind
&& let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
{
if !map.contains_key(res) {

View file

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

View file

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

View file

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

View file

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

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() {
ty::Array(sub_type, len) => match sub_type.kind() {
ty::Float(FloatTy::F32) => match len.to_valtree().try_to_machine_usize(tcx) {
ty::Float(FloatTy::F32) => match len.kind().try_to_machine_usize(tcx) {
Some(len) => alloc
.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
.to_owned()
.chunks(4)
.map(|chunk| {
Some(Constant::F32(f32::from_le_bytes(
chunk.try_into().expect("this shouldn't happen"),
)))
})
.array_chunks::<4>()
.map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
.collect::<Option<Vec<Constant>>>()
.map(Constant::Vec),
_ => None,
},
ty::Float(FloatTy::F64) => match len.to_valtree().try_to_machine_usize(tcx) {
ty::Float(FloatTy::F64) => match len.kind().try_to_machine_usize(tcx) {
Some(len) => alloc
.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
.to_owned()
.chunks(8)
.map(|chunk| {
Some(Constant::F64(f64::from_le_bytes(
chunk.try_into().expect("this shouldn't happen"),
)))
})
.array_chunks::<8>()
.map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
.collect::<Option<Vec<Constant>>>()
.map(Constant::Vec),
_ => None,

View file

@ -155,13 +155,7 @@ where
});
}
pub fn span_lint_hir(
cx: &LateContext<'_>,
lint: &'static Lint,
hir_id: HirId,
sp: Span,
msg: &str,
) {
pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
let mut diag = diag.build(msg);
docs_link(&mut diag, lint);

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
[package]
name = "cargo_common_metadata"
name = "cargo_common_metadata_fail"
version = "0.1.0"
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`
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

View file

@ -1,5 +1,5 @@
[package]
name = "cargo_common_metadata"
name = "cargo_common_metadata_fail_publish"
version = "0.1.0"
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`
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

View file

@ -1,5 +1,5 @@
[package]
name = "cargo_common_metadata"
name = "cargo_common_metadata_fail_publish_true"
version = "0.1.0"
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`
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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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-unwrap-in-tests
allowed-scripts
arithmetic-allowed
array-size-threshold
avoid-breaking-exported-api
await-holding-invalid-types

27
tests/ui/arithmetic.fixed Normal file
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() {}

27
tests/ui/arithmetic.rs Normal file
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
use core::num::Wrapping;
#[allow(dead_code, unused_assignments)]
#[warn(clippy::assign_op_pattern)]
fn main() {
@ -18,4 +20,13 @@ fn main() {
a = 6 << a;
let mut s = String::new();
s += "bla";
// Issue #9180
let mut a = Wrapping(0u32);
a += Wrapping(1u32);
let mut v = vec![0u32, 1u32];
v[0] += v[1];
let mut v = vec![Wrapping(0u32), Wrapping(1u32)];
v[0] = v[0] + v[1];
let _ = || v[0] = v[0] + v[1];
}

View file

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

View file

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

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-windows
#![feature(rustc_attrs)]

View file

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

View file

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

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)
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

@ -1,7 +1,6 @@
#![allow(dead_code)]
#![warn(clippy::expl_impl_clone_on_copy)]
#[derive(Copy)]
struct Qux;

View file

@ -1,5 +1,5 @@
error: you are implementing `Clone` explicitly on a `Copy` type
--> $DIR/derive.rs:8:1
--> $DIR/derive.rs:7:1
|
LL | / impl Clone for Qux {
LL | | fn clone(&self) -> Self {
@ -10,7 +10,7 @@ LL | | }
|
= note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings`
note: consider deriving `Clone` or removing `Copy`
--> $DIR/derive.rs:8:1
--> $DIR/derive.rs:7:1
|
LL | / impl Clone for Qux {
LL | | fn clone(&self) -> Self {
@ -20,7 +20,7 @@ LL | | }
| |_^
error: you are implementing `Clone` explicitly on a `Copy` type
--> $DIR/derive.rs:32:1
--> $DIR/derive.rs:31:1
|
LL | / impl<'a> Clone for Lt<'a> {
LL | | fn clone(&self) -> Self {
@ -30,7 +30,7 @@ LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
--> $DIR/derive.rs:32:1
--> $DIR/derive.rs:31:1
|
LL | / impl<'a> Clone for Lt<'a> {
LL | | fn clone(&self) -> Self {
@ -40,7 +40,7 @@ LL | | }
| |_^
error: you are implementing `Clone` explicitly on a `Copy` type
--> $DIR/derive.rs:43:1
--> $DIR/derive.rs:42:1
|
LL | / impl Clone for BigArray {
LL | | fn clone(&self) -> Self {
@ -50,7 +50,7 @@ LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
--> $DIR/derive.rs:43:1
--> $DIR/derive.rs:42:1
|
LL | / impl Clone for BigArray {
LL | | fn clone(&self) -> Self {
@ -60,7 +60,7 @@ LL | | }
| |_^
error: you are implementing `Clone` explicitly on a `Copy` type
--> $DIR/derive.rs:54:1
--> $DIR/derive.rs:53:1
|
LL | / impl Clone for FnPtr {
LL | | fn clone(&self) -> Self {
@ -70,7 +70,7 @@ LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
--> $DIR/derive.rs:54:1
--> $DIR/derive.rs:53:1
|
LL | / impl Clone for FnPtr {
LL | | fn clone(&self) -> Self {
@ -80,7 +80,7 @@ LL | | }
| |_^
error: you are implementing `Clone` explicitly on a `Copy` type
--> $DIR/derive.rs:74:1
--> $DIR/derive.rs:73:1
|
LL | / impl<T: Clone> Clone for Generic2<T> {
LL | | fn clone(&self) -> Self {
@ -90,7 +90,7 @@ LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
--> $DIR/derive.rs:74:1
--> $DIR/derive.rs:73:1
|
LL | / impl<T: Clone> Clone for Generic2<T> {
LL | | fn clone(&self) -> Self {

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