let ct = (self.fld_c)(bound_const, ct.ty());
ty::fold::shift_vars(self.tcx, ct, self.current_index.as_u32())
}
- _ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self),
- _ => ct,
+ _ => ct.super_fold_with(self),
}
}
+
+ fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+ if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+ }
}
impl<'tcx> TyCtxt<'tcx> {
.tcx
.mk_const(ty::ConstS { kind: ty::ConstKind::Placeholder(p), ty: ct.ty() })
}
- _ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self),
- _ => ct,
+ _ => ct.super_fold_with(self),
}
}
+
+ fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+ if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+ }
}
// The inverse of `BoundVarReplacer`: replaces placeholders with the bound vars from which they came.
We're collecting our changelog from pull request descriptions.
If your PR only includes internal changes, you can just write
`changelog: none`. Otherwise, please write a short comment
-explaining your change. Also, it's helpful for us that
-the lint name is put into brackets `[]` and backticks `` ` ` ``,
-e.g. ``[`lint_name`]``.
+explaining your change.
-If your PR fixes an issue, you can add "fixes #issue_number" into this
+It's also helpful for us that the lint name is put within backticks (`` ` ` ``),
+and then encapsulated by square brackets (`[]`), for example:
+```
+changelog: [`lint_name`]: your change
+```
+
+If your PR fixes an issue, you can add `fixes #issue_number` into this
PR description. This way the issue will be automatically closed when
your PR is merged.
OS: ${{ runner.os }}
metadata_collection:
- needs: base
+ needs: changelog
runs-on: ubuntu-latest
steps:
name: bors test finished
if: github.event.pusher.name == 'bors' && success()
runs-on: ubuntu-latest
- needs: [changelog, base, integration_build, integration]
+ needs: [changelog, base, metadata_collection, integration_build, integration]
steps:
- name: Mark the job as successful
name: bors test finished
if: github.event.pusher.name == 'bors' && (failure() || cancelled())
runs-on: ubuntu-latest
- needs: [changelog, base, integration_build, integration]
+ needs: [changelog, base, metadata_collection, integration_build, integration]
steps:
- name: Mark the job as a failure
# Changelog
All notable changes to this project will be documented in this file.
-See [Changelog Update](doc/changelog_update.md) if you want to update this
+See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this
document.
## Unreleased / In Rust Nightly
-[d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master)
+[7c21f91b...master](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...master)
+
+## Rust 1.62
+
+Current stable, released 2022-06-30
+
+[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
+
+### New Lints
+
+* [`large_include_file`]
+ [#8727](https://github.com/rust-lang/rust-clippy/pull/8727)
+* [`cast_abs_to_unsigned`]
+ [#8635](https://github.com/rust-lang/rust-clippy/pull/8635)
+* [`err_expect`]
+ [#8606](https://github.com/rust-lang/rust-clippy/pull/8606)
+* [`unnecessary_owned_empty_strings`]
+ [#8660](https://github.com/rust-lang/rust-clippy/pull/8660)
+* [`empty_structs_with_brackets`]
+ [#8594](https://github.com/rust-lang/rust-clippy/pull/8594)
+* [`crate_in_macro_def`]
+ [#8576](https://github.com/rust-lang/rust-clippy/pull/8576)
+* [`needless_option_take`]
+ [#8665](https://github.com/rust-lang/rust-clippy/pull/8665)
+* [`bytes_count_to_len`]
+ [#8711](https://github.com/rust-lang/rust-clippy/pull/8711)
+* [`is_digit_ascii_radix`]
+ [#8624](https://github.com/rust-lang/rust-clippy/pull/8624)
+* [`await_holding_invalid_type`]
+ [#8707](https://github.com/rust-lang/rust-clippy/pull/8707)
+* [`trim_split_whitespace`]
+ [#8575](https://github.com/rust-lang/rust-clippy/pull/8575)
+* [`pub_use`]
+ [#8670](https://github.com/rust-lang/rust-clippy/pull/8670)
+* [`format_push_string`]
+ [#8626](https://github.com/rust-lang/rust-clippy/pull/8626)
+* [`empty_drop`]
+ [#8571](https://github.com/rust-lang/rust-clippy/pull/8571)
+* [`drop_non_drop`]
+ [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
+* [`forget_non_drop`]
+ [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
+
+### Moves and Deprecations
+
+* Move [`only_used_in_recursion`] to `nursery` (now allow-by-default)
+ [#8783](https://github.com/rust-lang/rust-clippy/pull/8783)
+* Move [`stable_sort_primitive`] to `pedantic` (now allow-by-default)
+ [#8716](https://github.com/rust-lang/rust-clippy/pull/8716)
+
+### Enhancements
+
+* Remove overlap between [`manual_split_once`] and [`needless_splitn`]
+ [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
+* [`map_identity`]: Now checks for needless `map_err`
+ [#8487](https://github.com/rust-lang/rust-clippy/pull/8487)
+* [`extra_unused_lifetimes`]: Now checks for impl lifetimes
+ [#8737](https://github.com/rust-lang/rust-clippy/pull/8737)
+* [`cast_possible_truncation`]: Now catches more cases with larger shift or divide operations
+ [#8687](https://github.com/rust-lang/rust-clippy/pull/8687)
+* [`identity_op`]: Now checks for modulo expressions
+ [#8519](https://github.com/rust-lang/rust-clippy/pull/8519)
+* [`panic`]: No longer lint in constant context
+ [#8592](https://github.com/rust-lang/rust-clippy/pull/8592)
+* [`manual_split_once`]: Now lints manual iteration of `splitn`
+ [#8717](https://github.com/rust-lang/rust-clippy/pull/8717)
+* [`self_named_module_files`], [`mod_module_files`]: Now handle relative module paths
+ [#8611](https://github.com/rust-lang/rust-clippy/pull/8611)
+* [`unsound_collection_transmute`]: Now has better size and alignment checks
+ [#8648](https://github.com/rust-lang/rust-clippy/pull/8648)
+* [`unnested_or_patterns`]: Ignore cases, where the suggestion would be longer
+ [#8619](https://github.com/rust-lang/rust-clippy/pull/8619)
+
+### False Positive Fixes
+
+* [`rest_pat_in_fully_bound_structs`]: Now ignores structs marked with `#[non_exhaustive]`
+ [#8690](https://github.com/rust-lang/rust-clippy/pull/8690)
+* [`needless_late_init`]: No longer lints `if let` statements, `let mut` bindings or instances that
+ changes the drop order significantly
+ [#8617](https://github.com/rust-lang/rust-clippy/pull/8617)
+* [`unnecessary_cast`]: No longer lints to casts to aliased or non-primitive types
+ [#8596](https://github.com/rust-lang/rust-clippy/pull/8596)
+* [`init_numbered_fields`]: No longer lints type aliases
+ [#8780](https://github.com/rust-lang/rust-clippy/pull/8780)
+* [`needless_option_as_deref`]: No longer lints for `as_deref_mut` on `Option` values that can't be moved
+ [#8646](https://github.com/rust-lang/rust-clippy/pull/8646)
+* [`mistyped_literal_suffixes`]: Now ignores float literals without an exponent
+ [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
+* [`undocumented_unsafe_blocks`]: Now ignores unsafe blocks from proc-macros and works better for sub-expressions
+ [#8450](https://github.com/rust-lang/rust-clippy/pull/8450)
+* [`same_functions_in_if_condition`]: Now allows different constants, even if they have the same value
+ [#8673](https://github.com/rust-lang/rust-clippy/pull/8673)
+* [`needless_match`]: Now checks for more complex types and ignores type coercion
+ [#8549](https://github.com/rust-lang/rust-clippy/pull/8549)
+* [`assertions_on_constants`]: Now ignores constants from `cfg!` macros
+ [#8614](https://github.com/rust-lang/rust-clippy/pull/8614)
+* [`indexing_slicing`]: Fix false positives with constant indices in
+ [#8588](https://github.com/rust-lang/rust-clippy/pull/8588)
+* [`iter_with_drain`]: Now ignores iterator references
+ [#8668](https://github.com/rust-lang/rust-clippy/pull/8668)
+* [`useless_attribute`]: Now allows [`redundant_pub_crate`] on `use` items
+ [#8743](https://github.com/rust-lang/rust-clippy/pull/8743)
+* [`cast_ptr_alignment`]: Now ignores expressions, when used for unaligned reads and writes
+ [#8632](https://github.com/rust-lang/rust-clippy/pull/8632)
+* [`wrong_self_convention`]: Now allows `&mut self` and no self as arguments for `is_*` methods
+ [#8738](https://github.com/rust-lang/rust-clippy/pull/8738)
+* [`mut_from_ref`]: Only lint in unsafe code
+ [#8647](https://github.com/rust-lang/rust-clippy/pull/8647)
+* [`redundant_pub_crate`]: Now allows macro exports
+ [#8736](https://github.com/rust-lang/rust-clippy/pull/8736)
+* [`needless_match`]: Ignores cases where the else block expression is different
+ [#8700](https://github.com/rust-lang/rust-clippy/pull/8700)
+* [`transmute_int_to_char`]: Now allows transmutations in `const` code
+ [#8610](https://github.com/rust-lang/rust-clippy/pull/8610)
+* [`manual_non_exhaustive`]: Ignores cases, where the enum value is used
+ [#8645](https://github.com/rust-lang/rust-clippy/pull/8645)
+* [`redundant_closure`]: Now ignores coerced closure
+ [#8431](https://github.com/rust-lang/rust-clippy/pull/8431)
+* [`identity_op`]: Is now ignored in cases where extra brackets would be needed
+ [#8730](https://github.com/rust-lang/rust-clippy/pull/8730)
+* [`let_unit_value`]: Now ignores cases which are used for type inference
+ [#8563](https://github.com/rust-lang/rust-clippy/pull/8563)
+
+### Suggestion Fixes/Improvements
+
+* [`manual_split_once`]: Fixed incorrect suggestions for single result accesses
+ [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
+* [`bytes_nth`]: Fix typos in the diagnostic message
+ [#8403](https://github.com/rust-lang/rust-clippy/pull/8403)
+* [`mistyped_literal_suffixes`]: Now suggests the correct integer types
+ [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
+* [`unnecessary_to_owned`]: Fixed suggestion based on the configured msrv
+ [#8692](https://github.com/rust-lang/rust-clippy/pull/8692)
+* [`single_element_loop`]: Improve lint for Edition 2021 arrays
+ [#8616](https://github.com/rust-lang/rust-clippy/pull/8616)
+* [`manual_bits`]: Now includes a cast for proper type conversion, when needed
+ [#8677](https://github.com/rust-lang/rust-clippy/pull/8677)
+* [`option_map_unit_fn`], [`result_map_unit_fn`]: Fix some incorrect suggestions
+ [#8584](https://github.com/rust-lang/rust-clippy/pull/8584)
+* [`collapsible_else_if`]: Add whitespace in suggestion
+ [#8729](https://github.com/rust-lang/rust-clippy/pull/8729)
+* [`transmute_bytes_to_str`]: Now suggest `from_utf8_unchecked` in `const` context
+ [#8612](https://github.com/rust-lang/rust-clippy/pull/8612)
+* [`map_clone`]: Improve message and suggestion based on the msrv
+ [#8688](https://github.com/rust-lang/rust-clippy/pull/8688)
+* [`needless_late_init`]: Now shows the `let` statement where it was first initialized
+ [#8779](https://github.com/rust-lang/rust-clippy/pull/8779)
+
+### ICE Fixes
+
+* [`only_used_in_recursion`]
+ [#8691](https://github.com/rust-lang/rust-clippy/pull/8691)
+* [`cast_slice_different_sizes`]
+ [#8720](https://github.com/rust-lang/rust-clippy/pull/8720)
+* [`iter_overeager_cloned`]
+ [#8602](https://github.com/rust-lang/rust-clippy/pull/8602)
+* [`undocumented_unsafe_blocks`]
+ [#8686](https://github.com/rust-lang/rust-clippy/pull/8686)
## Rust 1.61
-Current stable, released 2022-05-19
+Released 2022-05-19
[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481)
* [`needless_borrow`]: Prevent mutable borrows being moved and suggest removing the borrow on method calls
[#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
-* [`chars_next_cmp`]: Correctly excapes the suggestion
+* [`chars_next_cmp`]: Correctly escapes the suggestion
[#8376](https://github.com/rust-lang/rust-clippy/pull/8376)
* [`explicit_write`]: Add suggestions for `write!`s with format arguments
[#8365](https://github.com/rust-lang/rust-clippy/pull/8365)
* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
[#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
-[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/doc/roadmap-2021.md
+[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/proposals/roadmap-2021.md
[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
## Rust 1.50
* [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
* [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
* [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
-* Move `{unnnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
+* Move `{unnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
[configuration file]: ./rust-clippy#configuration
[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
-[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
+[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md
[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md
<!-- lint disable no-unused-definitions -->
<!-- begin autogenerated links to lint list -->
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
+[`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core
[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
[`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
[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
+[`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
+[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
+[`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core
[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
- [The Clippy book](#the-clippy-book)
- [High level approach](#high-level-approach)
- [Finding something to fix/improve](#finding-something-to-fiximprove)
- - [Writing code](#writing-code)
- [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
- [IntelliJ Rust](#intellij-rust)
- [Rust Analyzer](#rust-analyzer)
using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippy's `Cargo.toml.`
You will require a `nightly` toolchain with the `rustc-dev` component installed.
Make sure that in the `rust-analyzer` configuration, you set
-```
-{ "rust-analyzer.rustcSource": "discover" }
+```json
+{ "rust-analyzer.rustc.source": "discover" }
```
and
-```
+```json
{ "rust-analyzer.updates.channel": "nightly" }
```
You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also
# Clippy Book
This is the source for the Clippy Book. See the
-[book](src/infrastructure/book.md) for more information.
+[book](src/development/infrastructure/book.md) for more information.
includes how to build and test Clippy. For a more in depth description on the
codebase take a look at [Adding Lints] or [Common Tools].
-[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
-[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md
+[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md
+[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md
- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy)
- [Get the Code](#get-the-code)
# only run UI tests starting with `test_`
TESTNAME="test_" cargo uitest
# only run dogfood tests
-cargo test --test dogfood
+cargo dev dogfood
```
If the output of a [UI test] differs from the expected output, you can update
cargo dev update_lints
# create a new lint and register it
cargo dev new_lint
+# deprecate a lint and attempt to remove code relating to it
+cargo dev deprecate
# automatically formatting all code before each commit
cargo dev setup git-hook
# (experimental) Setup Clippy to work with IntelliJ-Rust
cargo dev setup intellij
+# runs the `dogfood` tests
+cargo dev dogfood
```
More about intellij command usage and reasons
Please also be sure to update the Beta/Unreleased sections at the top with the
relevant commit ranges.
+If you have the time, it would be appreciated if you double-check, that the
+`#[clippy::version]` attributes for the added lints contains the correct version.
+
[changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md
[forge]: https://forge.rust-lang.org/
[rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools/clippy
Clippy has lint groups which are allow-by-default. This means, that you will
have to enable the lints in those groups manually.
-For a full list of all lints with their description and examples, please refere
+For a full list of all lints with their description and examples, please refer
to [Clippy's lint list]. The two most important allow-by-default groups are
described below:
--- /dev/null
+use crate::clippy_project_root;
+use std::process::Command;
+
+/// # Panics
+///
+/// Panics if unable to run the dogfood test
+pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool) {
+ let mut cmd = Command::new("cargo");
+
+ cmd.current_dir(clippy_project_root())
+ .args(["test", "--test", "dogfood"])
+ .args(["--features", "internal"])
+ .args(["--", "dogfood_clippy"]);
+
+ let mut dogfood_args = Vec::new();
+ if fix {
+ dogfood_args.push("--fix");
+ }
+
+ if allow_dirty {
+ dogfood_args.push("--allow-dirty");
+ }
+
+ if allow_staged {
+ dogfood_args.push("--allow-staged");
+ }
+
+ cmd.env("__CLIPPY_DOGFOOD_ARGS", dogfood_args.join(" "));
+
+ let output = cmd.output().expect("failed to run command");
+
+ println!("{}", String::from_utf8_lossy(&output.stdout));
+}
use std::path::PathBuf;
pub mod bless;
+pub mod dogfood;
pub mod fmt;
pub mod lint;
pub mod new_lint;
#![warn(rust_2018_idioms, unused_lifetimes)]
use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue};
-use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
+use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints};
use indoc::indoc;
fn main() {
Some(("bless", matches)) => {
bless::bless(matches.contains_id("ignore-timestamp"));
},
+ Some(("dogfood", matches)) => {
+ dogfood::dogfood(
+ matches.contains_id("fix"),
+ matches.contains_id("allow-dirty"),
+ matches.contains_id("allow-staged"),
+ );
+ },
Some(("fmt", matches)) => {
fmt::run(matches.contains_id("check"), matches.contains_id("verbose"));
},
.long("ignore-timestamp")
.help("Include files updated before clippy was built"),
),
+ Command::new("dogfood").about("Runs the dogfood test").args([
+ Arg::new("fix").long("fix").help("Apply the suggestions when possible"),
+ Arg::new("allow-dirty")
+ .long("allow-dirty")
+ .help("Fix code even if the working directory has changes")
+ .requires("fix"),
+ Arg::new("allow-staged")
+ .long("allow-staged")
+ .help("Fix code even if the working directory has staged changes")
+ .requires("fix"),
+ ]),
Command::new("fmt")
.about("Run rustfmt on all projects and tests")
.args([
pos = m.end();
}
result.push_str(&contents[pos..]);
- edited.then(|| result)
+ edited.then_some(result)
}
fn round_to_fifty(count: usize) -> usize {
/// baz().await; // Lint violation
/// }
/// ```
- #[clippy::version = "1.49.0"]
+ #[clippy::version = "1.62.0"]
pub AWAIT_HOLDING_INVALID_TYPE,
suspicious,
"holding a type across an await point which is not allowed to be held as per the configuration"
/// ```
/// let x = &12;
/// let addr_x = &x as *const _ as usize;
- /// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggerd.
+ /// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggered.
/// // But if we fix it, assert will fail.
/// assert_ne!(addr_x, addr_y);
/// ```
/// let x: i32 = -42;
/// let y: u32 = x.unsigned_abs();
/// ```
- #[clippy::version = "1.61.0"]
+ #[clippy::version = "1.62.0"]
pub CAST_ABS_TO_UNSIGNED,
suspicious,
"casting the result of `abs()` to an unsigned integer can panic"
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
+use clippy_utils::ty::needs_ordered_drop;
+use clippy_utils::visitors::for_each_expr;
use clippy_utils::{
- eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed,
- search_same, ContainsName, HirEqInterExpr, SpanlessEq,
+ capture_local_usage, eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause,
+ is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
};
use core::iter;
+use core::ops::ControlFlow;
use rustc_errors::Applicability;
use rustc_hir::intravisit;
-use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Stmt, StmtKind};
+use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::hygiene::walk_chain;
fn lint_branches_sharing_code<'tcx>(
cx: &LateContext<'tcx>,
conds: &[&'tcx Expr<'_>],
- blocks: &[&Block<'tcx>],
+ blocks: &[&'tcx Block<'_>],
expr: &'tcx Expr<'_>,
) {
// We only lint ifs with multiple blocks
}
}
+/// Checks if the statement modifies or moves any of the given locals.
+fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool {
+ for_each_expr(s, |e| {
+ if let Some(id) = path_to_local(e)
+ && locals.contains(&id)
+ && !capture_local_usage(cx, e).is_imm_ref()
+ {
+ ControlFlow::Break(())
+ } else {
+ ControlFlow::Continue(())
+ }
+ })
+ .is_some()
+}
+
/// Checks if the given statement should be considered equal to the statement in the same position
/// for each block.
fn eq_stmts(
.all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt)))
}
-fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<'_>, blocks: &[&Block<'_>]) -> BlockEq {
+#[expect(clippy::too_many_lines)]
+fn scan_block_for_eq<'tcx>(
+ cx: &LateContext<'tcx>,
+ conds: &[&'tcx Expr<'_>],
+ block: &'tcx Block<'_>,
+ blocks: &[&'tcx Block<'_>],
+) -> BlockEq {
let mut eq = SpanlessEq::new(cx);
let mut eq = eq.inter_expr();
let mut moved_locals = Vec::new();
+ let mut cond_locals = HirIdSet::default();
+ for &cond in conds {
+ let _: Option<!> = for_each_expr(cond, |e| {
+ if let Some(id) = path_to_local(e) {
+ cond_locals.insert(id);
+ }
+ ControlFlow::Continue(())
+ });
+ }
+
+ let mut local_needs_ordered_drop = false;
let start_end_eq = block
.stmts
.iter()
.enumerate()
- .find(|&(i, stmt)| !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals))
+ .find(|&(i, stmt)| {
+ if let StmtKind::Local(l) = stmt.kind
+ && needs_ordered_drop(cx, cx.typeck_results().node_type(l.hir_id))
+ {
+ local_needs_ordered_drop = true;
+ return true;
+ }
+ modifies_any_local(cx, stmt, &cond_locals)
+ || !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals)
+ })
.map_or(block.stmts.len(), |(i, _)| i);
+ if local_needs_ordered_drop {
+ return BlockEq {
+ start_end_eq,
+ end_begin_eq: None,
+ moved_locals,
+ };
+ }
+
// Walk backwards through the final expression/statements so long as their hashes are equal. Note
// `SpanlessHash` treats all local references as equal allowing locals declared earlier in the block
// to match those in other blocks. e.g. If each block ends with the following the hash value will be
/// #[allow(clippy::crate_in_macro_def)]
/// macro_rules! ok { ... crate::foo ... }
/// ```
- #[clippy::version = "1.61.0"]
+ #[clippy::version = "1.62.0"]
pub CRATE_IN_MACRO_DEF,
suspicious,
"using `crate` in a macro definition"
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_ty, Visitor};
use rustc_hir::{
- self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, GenericArg, HirId, ImplItem,
- ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
+ self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId,
+ ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
TraitItemKind, TyKind, UnOp,
};
use rustc_infer::infer::TyCtxtInferExt;
Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
ExprKind::Ret(_) => {
- let output = cx
- .tcx
- .fn_sig(cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()))
- .skip_binder()
- .output();
- Some(if !output.is_ref() {
- Position::Other(precedence)
- } else if output.has_placeholders() || output.has_opaque_types() {
- Position::ReborrowStable(precedence)
- } else {
- Position::DerefStable(precedence)
- })
+ let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
+ Some(
+ if let Node::Expr(Expr {
+ kind: ExprKind::Closure(&Closure { fn_decl, .. }),
+ ..
+ }) = cx.tcx.hir().get(owner_id)
+ {
+ match fn_decl.output {
+ FnRetTy::Return(ty) => binding_ty_auto_deref_stability(ty, precedence),
+ FnRetTy::DefaultReturn(_) => Position::Other(precedence),
+ }
+ } else {
+ let output = cx
+ .tcx
+ .fn_sig(cx.tcx.hir().local_def_id(owner_id))
+ .skip_binder()
+ .output();
+ if !output.is_ref() {
+ Position::Other(precedence)
+ } else if output.has_placeholders() || output.has_opaque_types() {
+ Position::ReborrowStable(precedence)
+ } else {
+ Position::DerefStable(precedence)
+ }
+ },
+ )
+ },
+ ExprKind::Call(func, _) if func.hir_id == child_id => {
+ (child_id == e.hir_id).then_some(Position::Callee)
},
- ExprKind::Call(func, _) if func.hir_id == child_id => (child_id == e.hir_id).then(|| Position::Callee),
ExprKind::Call(func, args) => args
.iter()
.position(|arg| arg.hir_id == child_id)
} else if let Some(trait_id) = cx.tcx.trait_of_item(id)
&& let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
&& let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
- && let subs = cx.typeck_results().node_substs_opt(child_id).unwrap_or_else(
- || cx.tcx.mk_substs([].iter())
- ) && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
+ && let subs = match cx
+ .typeck_results()
+ .node_substs_opt(parent.hir_id)
+ .and_then(|subs| subs.get(1..))
+ {
+ Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
+ None => cx.tcx.mk_substs([].iter()),
+ } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
// Trait methods taking `&self`
sub_ty
} else {
/// i_am_eq_too: Vec<String>,
/// }
/// ```
- #[clippy::version = "1.62.0"]
+ #[clippy::version = "1.63.0"]
pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
style,
"deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
/// let x = Foo;
/// std::mem::drop(x);
/// ```
- #[clippy::version = "1.61.0"]
+ #[clippy::version = "1.62.0"]
pub DROP_NON_DROP,
suspicious,
"call to `std::mem::drop` with a value which does not implement `Drop`"
/// let x = Foo;
/// std::mem::forget(x);
/// ```
- #[clippy::version = "1.61.0"]
+ #[clippy::version = "1.62.0"]
pub FORGET_NON_DROP,
suspicious,
"call to `std::mem::forget` with a value which does not implement `Drop`"
use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind};
use rustc_errors::MultiSpan;
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext, Level};
+use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{FileName, Span};
use std::collections::BTreeMap;
}
fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
- for Modules { local_path, spans, lint_levels } in self.modules.values() {
+ for Modules {
+ local_path,
+ spans,
+ lint_levels,
+ } in self.modules.values()
+ {
if spans.len() < 2 {
continue;
}
// At this point the lint would be emitted
assert_eq!(spans.len(), lint_levels.len());
- let spans: Vec<_> = spans.into_iter().zip(lint_levels).filter_map(|(span, lvl)|{
- if let Some(id) = lvl.get_expectation_id() {
- cx.fulfill_expectation(id);
- }
+ let spans: Vec<_> = spans
+ .iter()
+ .zip(lint_levels)
+ .filter_map(|(span, lvl)| {
+ if let Some(id) = lvl.get_expectation_id() {
+ cx.fulfill_expectation(id);
+ }
- (!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span)
- })
- .collect();
+ (!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span)
+ })
+ .collect();
if spans.len() < 2 {
continue;
/// ```rust
/// struct S;
/// ```
- #[clippy::version = "1.61.0"]
+ #[clippy::version = "1.62.0"]
pub EMPTY_DROP,
restriction,
"empty `Drop` implementations"
let allow_insert_closure = s.allow_insert_closure;
let is_single_insert = s.is_single_insert;
let edits = s.edits;
- s.can_use_entry.then(|| InsertSearchResults {
+ s.can_use_entry.then_some(InsertSearchResults {
edits,
allow_insert_closure,
is_single_insert,
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::Ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
impl<'tcx> LateLintPass<'tcx> for PatternEquality {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if_chain! {
+ if !in_external_macro(cx.sess(), expr.span);
if let ExprKind::Let(let_expr) = expr.kind;
if unary_pattern(let_expr.pat);
let exp_ty = cx.typeck_results().expr_ty(let_expr.init);
/// ### Why is this bad?
/// Introduces an extra, avoidable heap allocation.
///
+ /// ### Known problems
+ /// `format!` returns a `String` but `write!` returns a `Result`.
+ /// Thus you are forced to ignore the `Err` variant to achieve the same API.
+ ///
+ /// While using `write!` in the suggested way should never fail, this isn't necessarily clear to the programmer.
+ ///
/// ### Example
/// ```rust
/// let mut s = String::new();
/// let mut s = String::new();
/// let _ = write!(s, "0x{:X}", 1024);
/// ```
- #[clippy::version = "1.61.0"]
+ #[clippy::version = "1.62.0"]
pub FORMAT_PUSH_STRING,
- perf,
+ restriction,
"`format!(..)` appended to existing `String`"
}
declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);
(!span.from_expansion()
&& impl_item.generics.params.is_empty()
&& !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
- .then(|| span)
+ .then_some(span)
} else {
None
}
--- /dev/null
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_function_call, paths};
+use rustc_ast::{BorrowKind, LitKind};
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Spanned;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `std::str::from_utf8_unchecked` with an invalid UTF-8 literal
+ ///
+ /// ### Why is this bad?
+ /// Creating such a `str` would result in undefined behavior
+ ///
+ /// ### Example
+ /// ```rust
+ /// # #[allow(unused)]
+ /// unsafe {
+ /// std::str::from_utf8_unchecked(b"cl\x82ippy");
+ /// }
+ /// ```
+ #[clippy::version = "1.64.0"]
+ pub INVALID_UTF8_IN_UNCHECKED,
+ correctness,
+ "using a non UTF-8 literal in `std::std::from_utf8_unchecked`"
+}
+declare_lint_pass!(InvalidUtf8InUnchecked => [INVALID_UTF8_IN_UNCHECKED]);
+
+impl<'tcx> LateLintPass<'tcx> for InvalidUtf8InUnchecked {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ if let Some([arg]) = match_function_call(cx, expr, &paths::STR_FROM_UTF8_UNCHECKED) {
+ match &arg.kind {
+ ExprKind::Lit(Spanned { node: lit, .. }) => {
+ if let LitKind::ByteStr(bytes) = &lit
+ && std::str::from_utf8(bytes).is_err()
+ {
+ lint(cx, expr.span);
+ }
+ },
+ ExprKind::AddrOf(BorrowKind::Ref, _, Expr { kind: ExprKind::Array(args), .. }) => {
+ let elements = args.iter().map(|e|{
+ match &e.kind {
+ ExprKind::Lit(Spanned { node: lit, .. }) => match lit {
+ LitKind::Byte(b) => Some(*b),
+ #[allow(clippy::cast_possible_truncation)]
+ LitKind::Int(b, _) => Some(*b as u8),
+ _ => None
+ }
+ _ => None
+ }
+ }).collect::<Option<Vec<_>>>();
+
+ if let Some(elements) = elements
+ && std::str::from_utf8(&elements).is_err()
+ {
+ lint(cx, expr.span);
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+}
+
+fn lint(cx: &LateContext<'_>, span: Span) {
+ span_lint(
+ cx,
+ INVALID_UTF8_IN_UNCHECKED,
+ span,
+ "non UTF-8 literal in `std::str::from_utf8_unchecked`",
+ );
+}
/// For types that implement `Copy`, the suggestion to `Box` a variant's
/// data would require removing the trait impl. The types can of course
/// still be `Clone`, but that is worse ergonomically. Depending on the
- /// use case it may be possible to store the large data in an auxillary
+ /// use case it may be possible to store the large data in an auxiliary
/// structure (e.g. Arena or ECS).
///
/// The lint will ignore generic types if the layout depends on the
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
- LintId::of(format_push_string::FORMAT_PUSH_STRING),
LintId::of(formatting::POSSIBLE_MISSING_COMMA),
LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
LintId::of(int_plus_one::INT_PLUS_ONE),
+ LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(len_zero::COMPARISON_TO_EMPTY),
LintId::of(infinite_iter::INFINITE_ITER),
LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
+ LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
LintId::of(loops::ITER_NEXT_LOOP),
inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
int_plus_one::INT_PLUS_ONE,
invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
+ invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED,
items_after_statements::ITEMS_AFTER_STATEMENTS,
iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
large_const_arrays::LARGE_CONST_ARRAYS,
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
stable_sort_primitive::STABLE_SORT_PRIMITIVE,
+ std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
+ std_instead_of_core::STD_INSTEAD_OF_ALLOC,
+ std_instead_of_core::STD_INSTEAD_OF_CORE,
strings::STRING_ADD,
strings::STRING_ADD_ASSIGN,
strings::STRING_FROM_UTF8_AS_BYTES,
LintId::of(escape::BOXED_LOCAL),
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
- LintId::of(format_push_string::FORMAT_PUSH_STRING),
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(loops::MANUAL_MEMCPY),
LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
LintId::of(exit::EXIT),
LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
+ LintId::of(format_push_string::FORMAT_PUSH_STRING),
LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
LintId::of(implicit_return::IMPLICIT_RETURN),
LintId::of(indexing_slicing::INDEXING_SLICING),
LintId::of(shadow::SHADOW_SAME),
LintId::of(shadow::SHADOW_UNRELATED),
LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
+ LintId::of(std_instead_of_core::ALLOC_INSTEAD_OF_CORE),
+ LintId::of(std_instead_of_core::STD_INSTEAD_OF_ALLOC),
+ LintId::of(std_instead_of_core::STD_INSTEAD_OF_CORE),
LintId::of(strings::STRING_ADD),
LintId::of(strings::STRING_SLICE),
LintId::of(strings::STRING_TO_STRING),
mod inline_fn_without_body;
mod int_plus_one;
mod invalid_upcast_comparisons;
+mod invalid_utf8_in_unchecked;
mod items_after_statements;
mod iter_not_returning_iterator;
mod large_const_arrays;
mod size_of_in_element_count;
mod slow_vector_initialization;
mod stable_sort_primitive;
+mod std_instead_of_core;
mod strings;
mod strlen_on_c_strings;
mod suspicious_operation_groupings;
store.register_late_pass(move || Box::new(manual_retain::ManualRetain::new(msrv)));
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));
// add lints here, do not remove this comment, it's used in `new_lint`
}
if let Some((name, ty, initializer)) = initialize_visitor.get_result();
if is_integer_const(cx, initializer, 0);
then {
- let mut applicability = Applicability::MachineApplicable;
+ let mut applicability = Applicability::MaybeIncorrect;
+ let span = expr.span.with_hi(arg.span.hi());
let int_name = match ty.map(Ty::kind) {
// usize or inferred
span_lint_and_sugg(
cx,
EXPLICIT_COUNTER_LOOP,
- expr.span.with_hi(arg.span.hi()),
+ span,
&format!("the variable `{}` is used as a loop counter", name),
"consider using",
format!(
span_lint_and_then(
cx,
EXPLICIT_COUNTER_LOOP,
- expr.span.with_hi(arg.span.hi()),
+ span,
&format!("the variable `{}` is used as a loop counter", name),
|diag| {
diag.span_suggestion(
- expr.span.with_hi(arg.span.hi()),
+ span,
"consider using",
format!(
"for ({}, {}) in (0_{}..).zip({})",
if_chain! {
// This should be the loop
if let Some((node_hir, Node::Stmt(..))) = parent_iter.next();
- // This should be the funciton body
+ // This should be the function body
if let Some((_, Node::Block(block))) = parent_iter.next();
if let Some((last_stmt, last_ret)) = extract(block);
if last_stmt.hir_id == node_hir;
_ => ""
};
+ let sugg = format!("{arg_snippet}{copied}.flatten()");
+
+ // If suggestion is not a one-liner, it won't be shown inline within the error message. In that case,
+ // it will be shown in the extra `help` message at the end, which is why the first `help_msg` needs
+ // to refer to the correct relative position of the suggestion.
+ let help_msg = if sugg.contains('\n') {
+ "remove the `if let` statement in the for loop and then..."
+ } else {
+ "...and remove the `if let` statement in the for loop"
+ };
+
span_lint_and_then(
cx,
MANUAL_FLATTEN,
span,
&msg,
|diag| {
- let sugg = format!("{}{}.flatten()", arg_snippet, copied);
diag.span_suggestion(
arg.span,
"try",
sugg,
- Applicability::MaybeIncorrect,
+ applicability,
);
diag.span_help(
inner_expr.span,
- "...and remove the `if let` statement in the for loop",
+ help_msg,
);
}
);
use clippy_utils::higher;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{
- get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
+ get_enclosing_loop_or_multi_call_closure, is_refutable, is_trait_method, match_def_path, paths,
+ visitors::is_res_used,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{Closure, def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
+use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
use rustc_lint::LateContext;
+use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::ty::adjustment::Adjust;
use rustc_span::{symbol::sym, Symbol};
used_iter: bool,
}
impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
+ type NestedFilter = OnlyBodies;
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
if self.used_iter {
return;
used_after: bool,
}
impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
+ type NestedFilter = OnlyBodies;
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
fn visit_local(&mut self, l: &'tcx Local<'_>) {
if !self.after_loop {
l.pat.each_binding_or_first(&mut |_, id, _, _| {
}
}
- if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
- // The iterator expression will be used on the next iteration (for loops), or on the next call (for
- // closures) unless it is declared within the enclosing expression. TODO: Check for closures
- // used where an `FnOnce` type is expected.
+ if let Some(e) = get_enclosing_loop_or_multi_call_closure(cx, loop_expr) {
let local_id = match iter_expr.path {
Res::Local(id) => id,
_ => return true,
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
- AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId,
- IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
+ AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound,
+ HirId, IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
(matches!(v.data, hir::VariantData::Unit(_))
&& v.ident.as_str().starts_with('_')
&& is_doc_hidden(cx.tcx.hir().attrs(v.id)))
- .then(|| (id, v.span))
+ .then_some((id, v.span))
});
if let Some((id, span)) = iter.next()
&& iter.next().is_none()
&& let Some(const3) = check_for_unsigned_int_constant(cx, right)
// Also ensures the const is nonzero since zero can't be a divisor
&& const1 == const2 && const2 == const3
- && let Some(hir_id) = path_to_local(expr3) {
+ && let Some(hir_id) = path_to_local(expr3)
+ && let Some(Node::Pat(_)) = cx.tcx.hir().find(hir_id) {
// Apply only to params or locals with annotated types
match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
Some(Node::Param(..)) => (),
// Determine which binding mode to use.
let explicit_ref = some_pat.contains_explicit_ref_binding();
- let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
+ let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then_some(ty_mutability));
let as_ref_str = match binding_ref {
Some(Mutability::Mut) => ".as_mut()",
if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
let iter_without_last = iter.clone();
if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
- if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let);
- if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let);
+ if let Some(b0) = find_bool_lit(&first_expr.kind);
+ if let Some(b1) = find_bool_lit(&last_expr.kind);
if b0 != b1;
if first_guard.is_none() || iter.len() == 0;
if first_attrs.is_empty();
if iter
.all(|arm| {
- find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
+ find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
});
then {
if let Some(last_pat) = last_pat_opt {
}
/// Extract a `bool` or `{ bool }`
-fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
+fn find_bool_lit(ex: &ExprKind<'_>) -> Option<bool> {
match ex {
ExprKind::Lit(Spanned {
node: LitKind::Bool(b), ..
..
},
_,
- ) if is_if_let => {
+ ) => {
if let ExprKind::Lit(Spanned {
node: LitKind::Bool(b), ..
}) = exp.kind
normalized_pats[i + 1..]
.iter()
.enumerate()
- .find_map(|(j, other)| pat.has_overlapping_values(other).then(|| i + 1 + j))
+ .find_map(|(j, other)| pat.has_overlapping_values(other).then_some(i + 1 + j))
.unwrap_or(normalized_pats.len())
})
.collect();
.zip(forwards_blocking_idxs[..i].iter().copied().rev())
.skip_while(|&(_, forward_block)| forward_block > i)
.find_map(|((j, other), forward_block)| {
- (forward_block == i || pat.has_overlapping_values(other)).then(|| j)
+ (forward_block == i || pat.has_overlapping_values(other)).then_some(j)
})
.unwrap_or(0)
})
(Self::Slice(pats, None), Self::Slice(front, Some(back)))
| (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => {
// Here `pats` is an exact size match. If the combined lengths of `front` and `back` are greater
- // then the minium length required will be greater than the length of `pats`.
+ // then the minimum length required will be greater than the length of `pats`.
if pats.len() < front.len() + back.len() {
return false;
}
let start = scrutinee_span.hi();
let mut arm_spans = arms.iter().map(|arm| {
let data = arm.span.data();
- (data.ctxt == SyntaxContext::root()).then(|| (data.lo, data.hi))
+ (data.ctxt == SyntaxContext::root()).then_some((data.lo, data.hi))
});
let end = e.span.hi();
parent: None,
}
.span();
- (!span_contains_cfg(cx, span)).then(|| next_start).ok_or(())
+ (!span_contains_cfg(cx, span)).then_some(next_start).ok_or(())
});
match found {
Ok(start) => {
source: MatchSource,
) -> Option<(Vec<FoundSigDrop>, &'static str)> {
let mut helper = SigDropHelper::new(cx);
+ let scrutinee = match (source, &scrutinee.kind) {
+ (MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e,
+ _ => scrutinee,
+ };
helper.find_sig_drop(scrutinee).map(|drops| {
let message = if source == MatchSource::Normal {
"temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
use rustc_hir::def::Res;
use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp};
use rustc_lint::LateContext;
+use rustc_middle::ty::adjustment::Adjust;
use rustc_span::source_map::Span;
use rustc_span::symbol::{sym, Symbol};
use std::borrow::Cow;
is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some))
}
-/// lint use of `filter().map()` for `Iterators`
-fn lint_filter_some_map_unwrap(
+/// is `filter(|x| x.is_some()).map(|x| x.unwrap())`
+fn is_filter_some_map_unwrap(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
filter_recv: &hir::Expr<'_>,
filter_arg: &hir::Expr<'_>,
map_arg: &hir::Expr<'_>,
- target_span: Span,
- methods_span: Span,
-) {
+) -> bool {
let iterator = is_trait_method(cx, expr, sym::Iterator);
let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::Option);
- if (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) {
- let msg = "`filter` for `Some` followed by `unwrap`";
- let help = "consider using `flatten` instead";
- let sugg = format!(
- "{}",
- reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, target_span),)
- );
- span_lint_and_sugg(
- cx,
- OPTION_FILTER_MAP,
- methods_span,
- msg,
- help,
- sugg,
- Applicability::MachineApplicable,
- );
- }
+
+ (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg)
}
/// lint use of `filter().map()` or `find().map()` for `Iterators`
map_span: Span,
is_find: bool,
) {
- lint_filter_some_map_unwrap(
- cx,
- expr,
- filter_recv,
- filter_arg,
- map_arg,
- map_span,
- filter_span.with_hi(expr.span.hi()),
- );
+ if is_filter_some_map_unwrap(cx, expr, filter_recv, filter_arg, map_arg) {
+ span_lint_and_sugg(
+ cx,
+ OPTION_FILTER_MAP,
+ filter_span.with_hi(expr.span.hi()),
+ "`filter` for `Some` followed by `unwrap`",
+ "consider using `flatten` instead",
+ reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, map_span)).into_owned(),
+ Applicability::MachineApplicable,
+ );
+
+ return;
+ }
+
if_chain! {
if is_trait_method(cx, map_recv, sym::Iterator);
// closure ends with is_some() or is_ok()
if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
if let ExprKind::MethodCall(path, [filter_arg], _) = filter_body.value.kind;
- if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def();
+ if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def();
if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) {
Some(false)
} else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) {
if let ExprKind::MethodCall(seg, [map_arg, ..], _) = map_body.value.kind;
if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
+ // .filter(..).map(|y| f(y).copied().unwrap())
+ // ~~~~
+ let map_arg_peeled = match map_arg.kind {
+ ExprKind::MethodCall(method, [original_arg], _) if acceptable_methods(method) => {
+ original_arg
+ },
+ _ => map_arg,
+ };
+
+ // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap())
+ let simple_equal = path_to_local_id(filter_arg, filter_param_id)
+ && path_to_local_id(map_arg_peeled, map_param_id);
+
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
// in `filter(|x| ..)`, replace `*x` with `x`
let a_path = if_chain! {
then { expr_path } else { a }
};
// let the filter closure arg and the map closure arg be equal
- if_chain! {
- if path_to_local_id(a_path, filter_param_id);
- if path_to_local_id(b, map_param_id);
- if cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b);
- then {
- return true;
- }
- }
- false
- };
-
- if match map_arg.kind {
- ExprKind::MethodCall(method, [original_arg], _) => {
- acceptable_methods(method)
- && SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, original_arg)
- },
- _ => SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg)
+ path_to_local_id(a_path, filter_param_id)
+ && path_to_local_id(b, map_param_id)
+ && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b)
};
+ if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled);
then {
let span = filter_span.with_hi(expr.span.hi());
let (filter_name, lint) = if is_find {
} else {
("filter", MANUAL_FILTER_MAP)
};
- let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name);
- let to_opt = if is_result { ".ok()" } else { "" };
- let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident,
- snippet(cx, map_arg.span, ".."), to_opt);
+ let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`");
+ let (to_opt, deref) = if is_result {
+ (".ok()", String::new())
+ } else {
+ let derefs = cx.typeck_results()
+ .expr_adjustments(map_arg)
+ .iter()
+ .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
+ .count();
+
+ ("", "*".repeat(derefs))
+ };
+ let sugg = format!(
+ "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})",
+ snippet(cx, map_arg.span, ".."),
+ );
span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
}
}
Some(RepeatKind::String)
} else {
let ty = ty.peel_refs();
- (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)).then(|| RepeatKind::String)
+ (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)).then_some(RepeatKind::String)
}
}
}
/// let x: Result<u32, &str> = Ok(10);
/// x.expect_err("Testing expect_err");
/// ```
- #[clippy::version = "1.61.0"]
+ #[clippy::version = "1.62.0"]
pub ERR_EXPECT,
style,
r#"using `.err().expect("")` when `.expect_err("")` can be used"#
declare_clippy_lint! {
/// ### What it does
- /// Finds usages of [`char::is_digit`]
- /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
- /// can be replaced with [`is_ascii_digit`]
- /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
- /// [`is_ascii_hexdigit`]
- /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
+ /// Finds usages of [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
+ /// can be replaced with [`is_ascii_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
+ /// [`is_ascii_hexdigit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
///
/// ### Why is this bad?
/// `is_digit(..)` is slower and requires specifying the radix.
/// c.is_ascii_digit();
/// c.is_ascii_hexdigit();
/// ```
- #[clippy::version = "1.61.0"]
+ #[clippy::version = "1.62.0"]
pub IS_DIGIT_ASCII_RADIX,
style,
"use of `char::is_digit(..)` with literal radix of 10 or 16"
}
declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calling `take` function after `as_ref`.
///
/// ### Why is this bad?
+ /// Redundant code. `take` writes `None` to its argument.
+ /// In this case the modification is useless as it's a temporary that cannot be read from afterwards.
///
/// ### Example
/// ```rust
/// let x = Some(3);
/// x.as_ref();
/// ```
- #[clippy::version = "1.61.0"]
+ #[clippy::version = "1.62.0"]
pub NEEDLESS_OPTION_TAKE,
complexity,
"using `.as_ref().take()` on a temporary value"
}
},
("take", []) => needless_option_take::check(cx, expr, recv),
+ ("then", [arg]) => {
+ if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
+ return;
+ }
+ unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
+ },
("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
implicit_clone::check(cx, name, expr, recv);
},
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
-use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite};
+use clippy_utils::source::{snippet, snippet_with_macro_callsite};
use clippy_utils::ty::{implements_trait, match_type};
use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths};
use if_chain::if_chain;
cx: &LateContext<'_>,
name: &str,
fun: &hir::Expr<'_>,
- self_expr: &hir::Expr<'_>,
arg: &hir::Expr<'_>,
or_has_args: bool,
span: Span,
+ method_span: Span,
) -> bool {
let is_default_default = || is_trait_item(cx, fun, sym::Default);
|| (matches!(path, sym::new) && implements_default(arg, default_trait_id));
then {
- let mut applicability = Applicability::MachineApplicable;
- let hint = "unwrap_or_default()";
- let sugg_span = span;
-
- let sugg: String = format!(
- "{}.{}",
- snippet_with_applicability(cx, self_expr.span, "..", &mut applicability),
- hint
- );
-
span_lint_and_sugg(
cx,
OR_FUN_CALL,
- sugg_span,
+ method_span.with_hi(span.hi()),
&format!("use of `{}` followed by a call to `{}`", name, path),
"try this",
- sugg,
- applicability,
+ "unwrap_or_default()".to_string(),
+ Applicability::MachineApplicable,
);
true
match inner_arg.kind {
hir::ExprKind::Call(fun, or_args) => {
let or_has_args = !or_args.is_empty();
- if !check_unwrap_or_default(cx, name, fun, self_arg, arg, or_has_args, expr.span) {
+ if !check_unwrap_or_default(cx, name, fun, arg, or_has_args, expr.span, method_span) {
let fun_span = if or_has_args { None } else { Some(fun.span) };
check_general_case(cx, name, method_span, self_arg, arg, expr.span, fun_span);
}
) {
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
+ let is_bool = cx.typeck_results().expr_ty(recv).is_bool();
- if is_option || is_result {
+ if is_option || is_result || is_bool {
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind {
let body = cx.tcx.hir().body(body);
let body_expr = &body.value;
if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
let msg = if is_option {
"unnecessary closure used to substitute value for `Option::None`"
- } else {
+ } else if is_result {
"unnecessary closure used to substitute value for `Result::Err`"
+ } else {
+ "unnecessary closure used with `bool::then`"
};
let applicability = if body
.params
use rustc_span::hygiene::MacroKind;
if expr.span.from_expansion() {
let data = expr.span.ctxt().outer_expn_data();
- matches!(data.kind, ExpnKind::Macro(MacroKind::Attr|MacroKind::Derive, _))
+ matches!(data.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, _))
} else {
false
}
declare_clippy_lint! {
/// ### What it does
/// Checks for type parameters which are positioned inconsistently between
- /// a type definition and impl block. Specifically, a paramater in an impl
+ /// a type definition and impl block. Specifically, a parameter in an impl
/// block which has the same name as a parameter in the type def, but is in
/// a different place.
///
// shouldn't be implemented when it is hidden in docs
return;
}
- if impl_item
- .generics
- .params
- .iter()
- .any(|gen| matches!(gen.kind, hir::GenericParamKind::Type { .. }))
- {
- // when the result of `new()` depends on a type parameter we should not require
- // an
- // impl of `Default`
+ if !impl_item.generics.params.is_empty() {
+ // when the result of `new()` depends on a parameter we should not require
+ // an impl of `Default`
return;
}
if_chain! {
/// and friends since the string is already preprocessed when Clippy lints
/// can see it.
///
- /// # Example
+ /// ### Example
/// ```rust
/// let one = "\033[1m Bold? \033[0m"; // \033 intended as escape
/// let two = "\033\0"; // \033 intended as null-3-3
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
use rustc_hir::{
- Arm, Closure, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment,
- QPath, Stmt, StmtKind, TyKind, UnOp,
+ Arm, Block, Body, Closure, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path,
+ PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
});
if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind {
match some_captures.get(local_id)
- .or_else(|| (method_sugg == "map_or_else").then(|| ()).and_then(|_| none_captures.get(local_id)))
+ .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|_| none_captures.get(local_id)))
{
Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
.iter()
.filter_map(get_rptr_lm)
.filter(|&(lt, _, _)| cx.tcx.named_region(lt.hir_id) == out_region)
- .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span))
+ .map(|(_, mutability, span)| (mutability == Mutability::Not).then_some(span))
.collect();
if let Some(args) = args
&& !args.is_empty()
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{eq_expr_value, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt};
+use clippy_utils::{
+ eq_expr_value, get_parent_node, is_else_clause, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks,
+ peel_blocks_with_stmt,
+};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::LangItem::{OptionNone, OptionSome, ResultOk};
-use rustc_hir::{BindingAnnotation, Expr, ExprKind, PatKind};
+use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
+use rustc_hir::{BindingAnnotation, Expr, ExprKind, Node, PatKind, PathSegment, QPath};
use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::Ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
+use rustc_span::{sym, symbol::Symbol};
declare_clippy_lint! {
/// ### What it does
declare_lint_pass!(QuestionMark => [QUESTION_MARK]);
-impl QuestionMark {
- /// Checks if the given expression on the given context matches the following structure:
+enum IfBlockType<'hir> {
+ /// An `if x.is_xxx() { a } else { b } ` expression.
///
- /// ```ignore
- /// if option.is_none() {
- /// return None;
- /// }
- /// ```
- ///
- /// ```ignore
- /// if result.is_err() {
- /// return result;
- /// }
- /// ```
+ /// Contains: caller (x), caller_type, call_sym (is_xxx), if_then (a), if_else (b)
+ IfIs(
+ &'hir Expr<'hir>,
+ Ty<'hir>,
+ Symbol,
+ &'hir Expr<'hir>,
+ Option<&'hir Expr<'hir>>,
+ ),
+ /// An `if let Xxx(a) = b { c } else { d }` expression.
///
- /// If it matches, it will suggest to use the question mark operator instead
- fn check_is_none_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) {
- if_chain! {
- if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
- if let ExprKind::MethodCall(segment, args, _) = &cond.kind;
- if let Some(subject) = args.get(0);
- if (Self::option_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_none)) ||
- (Self::result_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_err));
- then {
- let mut applicability = Applicability::MachineApplicable;
- let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
- let mut replacement: Option<String> = None;
- if let Some(else_inner) = r#else {
- if eq_expr_value(cx, subject, peel_blocks(else_inner)) {
- replacement = Some(format!("Some({}?)", receiver_str));
- }
- } else if Self::moves_by_default(cx, subject)
- && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
- {
- replacement = Some(format!("{}.as_ref()?;", receiver_str));
- } else {
- replacement = Some(format!("{}?;", receiver_str));
- }
+ /// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c),
+ /// if_else (d)
+ IfLet(
+ &'hir QPath<'hir>,
+ Ty<'hir>,
+ Symbol,
+ &'hir Expr<'hir>,
+ &'hir Expr<'hir>,
+ Option<&'hir Expr<'hir>>,
+ ),
+}
- if let Some(replacement_str) = replacement {
- span_lint_and_sugg(
- cx,
- QUESTION_MARK,
- expr.span,
- "this block may be rewritten with the `?` operator",
- "replace it with",
- replacement_str,
- applicability,
- );
+/// Checks if the given expression on the given context matches the following structure:
+///
+/// ```ignore
+/// if option.is_none() {
+/// return None;
+/// }
+/// ```
+///
+/// ```ignore
+/// if result.is_err() {
+/// return result;
+/// }
+/// ```
+///
+/// If it matches, it will suggest to use the question mark operator instead
+fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+ if_chain! {
+ if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
+ if !is_else_clause(cx.tcx, expr);
+ if let ExprKind::MethodCall(segment, args, _) = &cond.kind;
+ if let Some(caller) = args.get(0);
+ let caller_ty = cx.typeck_results().expr_ty(caller);
+ let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else);
+ if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block);
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
+ let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx.at(caller.span), cx.param_env) &&
+ !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
+ let sugg = if let Some(else_inner) = r#else {
+ if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
+ format!("Some({}?)", receiver_str)
+ } else {
+ return;
}
- }
+ } else {
+ format!("{}{}?;", receiver_str, if by_ref { ".as_ref()" } else { "" })
+ };
+
+ span_lint_and_sugg(
+ cx,
+ QUESTION_MARK,
+ expr.span,
+ "this block may be rewritten with the `?` operator",
+ "replace it with",
+ sugg,
+ applicability,
+ );
}
}
+}
- fn check_if_let_some_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) {
- if_chain! {
- if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
- = higher::IfLet::hir(cx, expr);
- if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
- if (Self::option_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, OptionSome)) ||
- (Self::result_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, ResultOk));
-
- if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
+fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+ 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;
+ 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))
+ || is_early_return(sym::Result, cx, &if_block);
+ if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none();
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
- if path_to_local_id(peel_blocks(if_then), bind_id);
- then {
- let mut applicability = Applicability::MachineApplicable;
- let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
- let replacement = format!("{}{}?", receiver_str, if by_ref { ".as_ref()" } else { "" },);
-
- span_lint_and_sugg(
- cx,
- QUESTION_MARK,
- expr.span,
- "this if-let-else may be rewritten with the `?` operator",
- "replace it with",
- replacement,
- applicability,
- );
- }
+ let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_)));
+ let sugg = format!(
+ "{}{}?{}",
+ receiver_str,
+ if by_ref { ".as_ref()" } else { "" },
+ if requires_semi { ";" } else { "" }
+ );
+ span_lint_and_sugg(
+ cx,
+ QUESTION_MARK,
+ expr.span,
+ "this block may be rewritten with the `?` operator",
+ "replace it with",
+ sugg,
+ applicability,
+ );
}
}
+}
- fn result_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool {
- Self::is_result(cx, expr) && Self::expression_returns_unmodified_err(nested_expr, expr)
- }
-
- fn option_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool {
- Self::is_option(cx, expr) && Self::expression_returns_none(cx, nested_expr)
- }
-
- fn moves_by_default(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
- let expr_ty = cx.typeck_results().expr_ty(expression);
-
- !expr_ty.is_copy_modulo_regions(cx.tcx.at(expression.span), cx.param_env)
- }
-
- fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
- let expr_ty = cx.typeck_results().expr_ty(expression);
-
- is_type_diagnostic_item(cx, expr_ty, sym::Option)
- }
-
- fn is_result(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
- let expr_ty = cx.typeck_results().expr_ty(expression);
-
- is_type_diagnostic_item(cx, expr_ty, sym::Result)
- }
-
- fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
- match peel_blocks_with_stmt(expression).kind {
- ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
- ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
- _ => false,
- }
+fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_>) -> bool {
+ match *if_block {
+ IfBlockType::IfIs(caller, caller_ty, call_sym, if_then, _) => {
+ // If the block could be identified as `if x.is_none()/is_err()`,
+ // we then only need to check the if_then return to see if it is none/err.
+ is_type_diagnostic_item(cx, caller_ty, smbl)
+ && expr_return_none_or_err(smbl, cx, if_then, caller, None)
+ && match smbl {
+ sym::Option => call_sym == sym!(is_none),
+ sym::Result => call_sym == sym!(is_err),
+ _ => false,
+ }
+ },
+ IfBlockType::IfLet(qpath, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => {
+ is_type_diagnostic_item(cx, let_expr_ty, smbl)
+ && match smbl {
+ sym::Option => {
+ // We only need to check `if let Some(x) = option` not `if let None = option`,
+ // because the later one will be suggested as `if option.is_none()` thus causing conflict.
+ is_lang_ctor(cx, qpath, OptionSome)
+ && if_else.is_some()
+ && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None)
+ },
+ sym::Result => {
+ (is_lang_ctor(cx, qpath, ResultOk)
+ && if_else.is_some()
+ && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym)))
+ || is_lang_ctor(cx, qpath, ResultErr)
+ && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym))
+ },
+ _ => false,
+ }
+ },
}
+}
- fn expression_returns_unmodified_err(expr: &Expr<'_>, cond_expr: &Expr<'_>) -> bool {
- match peel_blocks_with_stmt(expr).kind {
- ExprKind::Ret(Some(ret_expr)) => Self::expression_returns_unmodified_err(ret_expr, cond_expr),
- ExprKind::Path(_) => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
+fn expr_return_none_or_err(
+ smbl: Symbol,
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ cond_expr: &Expr<'_>,
+ err_sym: Option<Symbol>,
+) -> bool {
+ match peel_blocks_with_stmt(expr).kind {
+ ExprKind::Ret(Some(ret_expr)) => expr_return_none_or_err(smbl, cx, ret_expr, cond_expr, err_sym),
+ ExprKind::Path(ref qpath) => match smbl {
+ sym::Option => is_lang_ctor(cx, qpath, OptionNone),
+ sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
_ => false,
- }
+ },
+ ExprKind::Call(call_expr, args_expr) => {
+ if_chain! {
+ if smbl == sym::Result;
+ if let ExprKind::Path(QPath::Resolved(_, path)) = &call_expr.kind;
+ if let Some(segment) = path.segments.first();
+ if let Some(err_sym) = err_sym;
+ if let Some(arg) = args_expr.first();
+ if let ExprKind::Path(QPath::Resolved(_, arg_path)) = &arg.kind;
+ if let Some(PathSegment { ident, .. }) = arg_path.segments.first();
+ then {
+ return segment.ident.name == sym::Err && err_sym == ident.name;
+ }
+ }
+ false
+ },
+ _ => false,
}
}
impl<'tcx> LateLintPass<'tcx> for QuestionMark {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- Self::check_is_none_or_err_and_early_return(cx, expr);
- Self::check_if_let_some_or_err_and_early_return(cx, expr);
+ check_is_none_or_err_and_early_return(cx, expr);
+ check_if_let_some_or_err_and_early_return(cx, expr);
}
}
///
/// ### Example
/// ```ignore
- /// Regex::new("|")
+ /// Regex::new("(")
/// ```
#[clippy::version = "pre 1.29.0"]
pub INVALID_REGEX,
#[derive(Default)]
pub(crate) struct Shadow {
- bindings: Vec<FxHashMap<Symbol, Vec<ItemLocalId>>>,
+ bindings: Vec<(FxHashMap<Symbol, Vec<ItemLocalId>>, LocalDefId)>,
}
impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
let HirId { owner, local_id } = id;
// get (or insert) the list of items for this owner and symbol
- let data = self.bindings.last_mut().unwrap();
+ let (ref mut data, scope_owner) = *self.bindings.last_mut().unwrap();
let items_with_name = data.entry(ident.name).or_default();
// check other bindings with the same name, most recently seen first
return;
}
- if is_shadow(cx, owner, prev, local_id) {
+ if is_shadow(cx, scope_owner, prev, local_id) {
let prev_hir_id = HirId { owner, local_id: prev };
lint_shadow(cx, pat, prev_hir_id, ident.span);
// only lint against the "nearest" shadowed binding
fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
let hir = cx.tcx.hir();
- if !matches!(
- hir.body_owner_kind(hir.body_owner_def_id(body.id())),
- BodyOwnerKind::Closure
- ) {
- self.bindings.push(FxHashMap::default());
+ let owner_id = hir.body_owner_def_id(body.id());
+ if !matches!(hir.body_owner_kind(owner_id), BodyOwnerKind::Closure) {
+ self.bindings.push((FxHashMap::default(), owner_id));
}
}
--- /dev/null
+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};
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Finds items imported through `std` when available through `core`.
+ ///
+ /// ### Why is this bad?
+ ///
+ /// Crates which have `no_std` compatibility may wish to ensure types are imported from core to ensure
+ /// disabling `std` does not cause the crate to fail to compile. This lint is also useful for crates
+ /// migrating to become `no_std` compatible.
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::hash::Hasher;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use core::hash::Hasher;
+ /// ```
+ #[clippy::version = "1.64.0"]
+ pub STD_INSTEAD_OF_CORE,
+ restriction,
+ "type is imported from std when available in core"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Finds items imported through `std` when available through `alloc`.
+ ///
+ /// ### Why is this bad?
+ ///
+ /// Crates which have `no_std` compatibility and require alloc may wish to ensure types are imported from
+ /// alloc to ensure disabling `std` does not cause the crate to fail to compile. This lint is also useful
+ /// for crates migrating to become `no_std` compatible.
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::vec::Vec;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # extern crate alloc;
+ /// use alloc::vec::Vec;
+ /// ```
+ #[clippy::version = "1.64.0"]
+ pub STD_INSTEAD_OF_ALLOC,
+ restriction,
+ "type is imported from std when available in alloc"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Finds items imported through `alloc` when available through `core`.
+ ///
+ /// ### 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
+ /// is also useful for crates migrating to become `no_std` compatible.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # extern crate alloc;
+ /// use alloc::slice::from_ref;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use core::slice::from_ref;
+ /// ```
+ #[clippy::version = "1.64.0"]
+ pub ALLOC_INSTEAD_OF_CORE,
+ restriction,
+ "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]);
+
+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),
+ );
+ }
+ }
+}
+
+/// Returns the first named segment of a [`Path`].
+///
+/// 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)
+ }
+}
&& let ExprKind::Unary(UnOp::Deref, derefed_expr) = borrowed_expr.kind
&& cx.typeck_results().expr_ty(derefed_expr).is_unsafe_ptr()
{
- (true, (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then(|| derefed_expr.span))
+ (true, (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then_some(derefed_expr.span))
} else {
(false, None)
}
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
use clippy_utils::{SpanlessEq, SpanlessHash};
use core::hash::{Hash, Hasher};
use if_chain::if_chain;
+use itertools::Itertools;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unhash::UnhashMap;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::{
- GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath, TraitItem, Ty, TyKind,
- WherePredicate,
+ GenericArg, GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath,
+ TraitBoundModifier, TraitItem, TraitRef, Ty, TyKind, WherePredicate,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
-use std::fmt::Write as _;
+use rustc_span::{BytePos, Span};
declare_clippy_lint! {
/// ### What it does
/// ```
#[clippy::version = "1.38.0"]
pub TYPE_REPETITION_IN_BOUNDS,
- pedantic,
- "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
+ nursery,
+ "types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
}
declare_clippy_lint! {
///
/// fn func<T>(arg: T) where T: Clone + Default {}
/// ```
+ ///
+ /// ```rust
+ /// fn foo<T: Default + Default>(bar: T) {}
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// fn foo<T: Default>(bar: T) {}
+ /// ```
+ ///
+ /// ```rust
+ /// fn foo<T>(bar: T) where T: Default + Default {}
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// fn foo<T>(bar: T) where T: Default {}
+ /// ```
#[clippy::version = "1.47.0"]
pub TRAIT_DUPLICATION_IN_BOUNDS,
- pedantic,
- "Check if the same trait bounds are specified twice during a function declaration"
+ nursery,
+ "check if the same trait bounds are specified more than once during a generic declaration"
}
#[derive(Copy, Clone)]
fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
self.check_type_repetition(cx, gen);
check_trait_bound_duplication(cx, gen);
+ check_bounds_or_where_duplication(cx, gen);
+ }
+
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+ // special handling for self trait bounds as these are not considered generics
+ // ie. trait Foo: Display {}
+ if let Item {
+ kind: ItemKind::Trait(_, _, _, bounds, ..),
+ ..
+ } = item
+ {
+ rollup_traits(cx, bounds, "these bounds contain repeated elements");
+ }
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
);
then {
- let mut hint_string = format!(
- "consider combining the bounds: `{}:",
- snippet(cx, p.bounded_ty.span, "_")
+ let trait_bounds = v
+ .iter()
+ .copied()
+ .chain(p.bounds.iter())
+ .filter_map(get_trait_info_from_bound)
+ .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability))
+ .join(" + ");
+ let hint_string = format!(
+ "consider combining the bounds: `{}: {}`",
+ snippet(cx, p.bounded_ty.span, "_"),
+ trait_bounds,
);
- for b in v.iter() {
- if let GenericBound::Trait(ref poly_trait_ref, _) = b {
- let path = &poly_trait_ref.trait_ref.path;
- let _ = write!(hint_string,
- " {} +",
- snippet_with_applicability(cx, path.span, "..", &mut applicability)
- );
- }
- }
- for b in p.bounds.iter() {
- if let GenericBound::Trait(ref poly_trait_ref, _) = b {
- let path = &poly_trait_ref.trait_ref.path;
- let _ = write!(hint_string,
- " {} +",
- snippet_with_applicability(cx, path.span, "..", &mut applicability)
- );
- }
- }
- hint_string.truncate(hint_string.len() - 2);
- hint_string.push('`');
span_lint_and_help(
cx,
TYPE_REPETITION_IN_BOUNDS,
}
}
+#[derive(PartialEq, Eq, Hash, Debug)]
+struct ComparableTraitRef(Res, Vec<Res>);
+
+fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
+ if gen.span.from_expansion() {
+ return;
+ }
+
+ for predicate in gen.predicates {
+ if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
+ let msg = if predicate.in_where_clause() {
+ "these where clauses contain repeated elements"
+ } else {
+ "these bounds contain repeated elements"
+ };
+ rollup_traits(cx, bound_predicate.bounds, msg);
+ }
+ }
+}
+
fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> {
- if let GenericBound::Trait(t, _) = bound {
- Some((t.trait_ref.path.res, t.trait_ref.path.segments, t.span))
+ if let GenericBound::Trait(t, tbm) = bound {
+ let trait_path = t.trait_ref.path;
+ let trait_span = {
+ let path_span = trait_path.span;
+ if let TraitBoundModifier::Maybe = tbm {
+ path_span.with_lo(path_span.lo() - BytePos(1)) // include the `?`
+ } else {
+ path_span
+ }
+ };
+ Some((trait_path.res, trait_path.segments, trait_span))
} else {
None
}
}
+
+// FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds
+fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
+ ComparableTraitRef(
+ trait_ref.path.res,
+ trait_ref
+ .path
+ .segments
+ .iter()
+ .filter_map(|segment| {
+ // get trait bound type arguments
+ Some(segment.args?.args.iter().filter_map(|arg| {
+ if_chain! {
+ if let GenericArg::Type(ty) = arg;
+ if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
+ then { return Some(path.res) }
+ }
+ None
+ }))
+ })
+ .flatten()
+ .collect(),
+ )
+}
+
+fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
+ let mut map = FxHashMap::default();
+ let mut repeated_res = false;
+
+ let only_comparable_trait_refs = |bound: &GenericBound<'_>| {
+ if let GenericBound::Trait(t, _) = bound {
+ Some((into_comparable_trait_ref(&t.trait_ref), t.span))
+ } else {
+ None
+ }
+ };
+
+ for bound in bounds.iter().filter_map(only_comparable_trait_refs) {
+ let (comparable_bound, span_direct) = bound;
+ if map.insert(comparable_bound, span_direct).is_some() {
+ repeated_res = true;
+ }
+ }
+
+ if_chain! {
+ if repeated_res;
+ if let [first_trait, .., last_trait] = bounds;
+ then {
+ let all_trait_span = first_trait.span().to(last_trait.span());
+
+ let mut traits = map.values()
+ .filter_map(|span| snippet_opt(cx, *span))
+ .collect::<Vec<_>>();
+ traits.sort_unstable();
+ let traits = traits.join(" + ");
+
+ span_lint_and_sugg(
+ cx,
+ TRAIT_DUPLICATION_IN_BOUNDS,
+ all_trait_span,
+ msg,
+ "try",
+ traits,
+ Applicability::MachineApplicable
+ );
+ }
+ }
+}
sym::String => "",
_ => "<..>",
};
+
+ let box_content = format!("{outer}{generic}", outer = item_type);
span_lint_and_help(
cx,
BOX_COLLECTION,
hir_ty.span,
&format!(
- "you seem to be trying to use `Box<{outer}{generic}>`. Consider using just `{outer}{generic}`",
- outer=item_type,
- generic = generic),
+ "you seem to be trying to use `Box<{box_content}>`. Consider using just `{box_content}`"),
None,
&format!(
- "`{outer}{generic}` is already on the heap, `Box<{outer}{generic}>` makes an extra allocation",
- outer=item_type,
- generic = generic)
+ "`{box_content}` is already on the heap, `Box<{box_content}>` makes an extra allocation")
);
true
} else {
fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Symbol> {
let param = qpath_generic_tys(qpath).next()?;
let id = path_def_id(cx, param)?;
- cx.tcx
- .get_diagnostic_name(id)
- .filter(|&name| matches!(name, sym::HashMap | sym::String | sym::Vec))
+ cx.tcx.get_diagnostic_name(id).filter(|&name| {
+ matches!(
+ name,
+ sym::HashMap
+ | sym::String
+ | sym::Vec
+ | sym::HashSet
+ | sym::VecDeque
+ | sym::LinkedList
+ | sym::BTreeMap
+ | sym::BTreeSet
+ | sym::BinaryHeap
+ )
+ })
}
}
}
+fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> {
+ let body = cx.enclosing_body?;
+ let map = cx.tcx.hir();
+ let mut span = map.body(body).value.span;
+ for (_, node) in map.parent_iter(body.hir_id) {
+ match node {
+ Node::Expr(e) => span = e.span,
+ Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (),
+ _ => break,
+ }
+ }
+ Some(span)
+}
+
fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
let source_map = cx.sess().source_map();
let ctxt = span.ctxt();
if ctxt == SyntaxContext::root()
- && let Some(body) = cx.enclosing_body
+ && let Some(search_span) = get_body_search_span(cx)
{
if let Ok(unsafe_line) = source_map.lookup_line(span.lo())
- && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root())
+ && let Some(body_span) = walk_span_to_context(search_span, SyntaxContext::root())
&& let Ok(body_line) = source_map.lookup_line(body_span.lo())
&& Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf)
&& let Some(src) = unsafe_line.sf.src.as_deref()
use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::get_parent_node;
use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::visitors::for_each_value_source;
+use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind};
+use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, Node, PatKind, QPath, TyKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty, TypeVisitable, TypeSuperVisitable, TypeVisitor};
+use rustc_middle::ty;
use super::LET_UNIT_VALUE;
-pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
- if let StmtKind::Local(local) = stmt.kind
- && let Some(init) = local.init
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
+ if let Some(init) = local.init
&& !local.pat.span.from_expansion()
- && !in_external_macro(cx.sess(), stmt.span)
+ && !in_external_macro(cx.sess(), local.span)
&& cx.typeck_results().pat_ty(local.pat).is_unit()
{
- let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) {
- ControlFlow::Continue(())
- } else {
- ControlFlow::Break(())
- }).is_continue();
-
- if needs_inferred {
- if !matches!(local.pat.kind, PatKind::Wild) {
+ if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer))
+ || matches!(local.pat.kind, PatKind::Tuple([], None)))
+ && expr_needs_inferred_result(cx, init)
+ {
+ if !matches!(local.pat.kind, PatKind::Wild | PatKind::Tuple([], None)) {
span_lint_and_then(
cx,
LET_UNIT_VALUE,
- stmt.span,
+ local.span,
"this let-binding has unit value",
|diag| {
- diag.span_suggestion(
- local.pat.span,
- "use a wild (`_`) binding",
- "_",
- Applicability::MaybeIncorrect, // snippet
- );
+ diag.span_suggestion(
+ local.pat.span,
+ "use a wild (`_`) binding",
+ "_",
+ Applicability::MaybeIncorrect, // snippet
+ );
},
);
}
span_lint_and_then(
cx,
LET_UNIT_VALUE,
- stmt.span,
+ local.span,
"this let-binding has unit value",
|diag| {
if let Some(expr) = &local.init {
let snip = snippet_with_macro_callsite(cx, expr.span, "()");
diag.span_suggestion(
- stmt.span,
+ local.span,
"omit the `let` binding",
- format!("{};", snip),
+ format!("{snip};"),
Applicability::MachineApplicable, // snippet
);
}
}
}
-fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
- let id = match e.kind {
+/// Checks sub-expressions which create the value returned by the given expression for whether
+/// return value inference is needed. This checks through locals to see if they also need inference
+/// at this point.
+///
+/// e.g.
+/// ```rust,ignore
+/// let bar = foo();
+/// let x: u32 = if true { baz() } else { bar };
+/// ```
+/// Here the sources of the value assigned to `x` would be `baz()`, and `foo()` via the
+/// initialization of `bar`. If both `foo` and `baz` have a return type which require type
+/// inference then this function would return `true`.
+fn expr_needs_inferred_result<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
+ // The locals used for initialization which have yet to be checked.
+ let mut locals_to_check = Vec::new();
+ // All the locals which have been added to `locals_to_check`. Needed to prevent cycles.
+ let mut seen_locals = HirIdSet::default();
+ if !each_value_source_needs_inference(cx, e, &mut locals_to_check, &mut seen_locals) {
+ return false;
+ }
+ while let Some(id) = locals_to_check.pop() {
+ if let Some(Node::Local(l)) = get_parent_node(cx.tcx, id) {
+ if !l.ty.map_or(true, |ty| matches!(ty.kind, TyKind::Infer)) {
+ return false;
+ }
+ if let Some(e) = l.init {
+ if !each_value_source_needs_inference(cx, e, &mut locals_to_check, &mut seen_locals) {
+ return false;
+ }
+ } else if for_each_local_assignment(cx, id, |e| {
+ if each_value_source_needs_inference(cx, e, &mut locals_to_check, &mut seen_locals) {
+ ControlFlow::Continue(())
+ } else {
+ ControlFlow::Break(())
+ }
+ })
+ .is_break()
+ {
+ return false;
+ }
+ }
+ }
+
+ true
+}
+
+fn each_value_source_needs_inference(
+ cx: &LateContext<'_>,
+ e: &Expr<'_>,
+ locals_to_check: &mut Vec<HirId>,
+ seen_locals: &mut HirIdSet,
+) -> bool {
+ for_each_value_source(e, &mut |e| {
+ if needs_inferred_result_ty(cx, e, locals_to_check, seen_locals) {
+ ControlFlow::Continue(())
+ } else {
+ ControlFlow::Break(())
+ }
+ })
+ .is_continue()
+}
+
+fn needs_inferred_result_ty(
+ cx: &LateContext<'_>,
+ e: &Expr<'_>,
+ locals_to_check: &mut Vec<HirId>,
+ seen_locals: &mut HirIdSet,
+) -> bool {
+ let (id, args) = match e.kind {
ExprKind::Call(
Expr {
kind: ExprKind::Path(ref path),
hir_id,
..
},
- _,
+ args,
) => match cx.qpath_res(path, *hir_id) {
- Res::Def(DefKind::AssocFn | DefKind::Fn, id) => id,
+ Res::Def(DefKind::AssocFn | DefKind::Fn, id) => (id, args),
_ => return false,
},
- ExprKind::MethodCall(..) => match cx.typeck_results().type_dependent_def_id(e.hir_id) {
- Some(id) => id,
+ ExprKind::MethodCall(_, args, _) => match cx.typeck_results().type_dependent_def_id(e.hir_id) {
+ Some(id) => (id, args),
None => return false,
},
+ ExprKind::Path(QPath::Resolved(None, path)) => {
+ if let Res::Local(id) = path.res
+ && seen_locals.insert(id)
+ {
+ locals_to_check.push(id);
+ }
+ return true;
+ },
_ => return false,
};
let sig = cx.tcx.fn_sig(id).skip_binder();
if let ty::Param(output_ty) = *sig.output().kind() {
- sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index))
+ sig.inputs().iter().zip(args).all(|(&ty, arg)| {
+ !ty.is_param(output_ty.index) || each_value_source_needs_inference(cx, arg, locals_to_check, seen_locals)
+ })
} else {
false
}
}
-
-fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool {
- struct Visitor(u32);
- impl<'tcx> TypeVisitor<'tcx> for Visitor {
- type BreakTy = ();
- fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
- if let ty::Param(ty) = *ty.kind() {
- if ty.index == self.0 {
- ControlFlow::BREAK
- } else {
- ControlFlow::CONTINUE
- }
- } else {
- ty.super_visit_with(self)
- }
- }
- }
- ty.visit_with(&mut Visitor(index)).is_break()
-}
mod unit_cmp;
mod utils;
-use rustc_hir::{Expr, Stmt};
+use rustc_hir::{Expr, Local};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]);
-impl LateLintPass<'_> for UnitTypes {
- fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
- let_unit_value::check(cx, stmt);
+impl<'tcx> LateLintPass<'tcx> for UnitTypes {
+ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+ let_unit_value::check(cx, local);
}
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
use rustc_hir::hir_id::CRATE_HIR_ID;
use rustc_hir::intravisit::Visitor;
use rustc_hir::{
- BinOpKind, Block, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, TyKind,
- UnOp,
+ BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty,
+ TyKind, UnOp,
};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter;
if let ExprKind::Call(func, and_then_args) = expr.kind;
if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
if and_then_args.len() == 5;
- if let ExprKind::Closure { body, .. } = &and_then_args[4].kind;
- let body = cx.tcx.hir().body(*body);
+ if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind;
+ let body = cx.tcx.hir().body(body);
let only_expr = peel_blocks_with_stmt(&body.value);
if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
if let ExprKind::Path(..) = span_call_args[0].kind;
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{
- self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
+ self as hir, def::DefKind, intravisit, intravisit::Visitor, Closure, ExprKind, Item, ItemKind, Mutability, QPath,
};
use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
use rustc_middle::hir::nested_filter;
fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
DEFAULT_LINT_LEVELS
.iter()
- .find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level))
+ .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level))
}
pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
}
fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
- if let ExprKind::Closure { body, .. } = closure_expr.kind {
+ if let ExprKind::Closure(&Closure { body, .. }) = closure_expr.kind {
let mut scanner = IsMultiSpanScanner::new(cx);
intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body));
return scanner.is_multi_part();
/// This visitor finds the highest applicability value in the visited expressions
struct ApplicabilityResolver<'a, 'hir> {
cx: &'a LateContext<'hir>,
- /// This is the index of hightest `Applicability` for `paths::APPLICABILITY_VALUES`
+ /// This is the index of highest `Applicability` for `paths::APPLICABILITY_VALUES`
applicability_index: Option<usize>,
}
args.push(arg, span);
}
- parser.errors.is_empty().then(move || args)
+ parser.errors.is_empty().then_some(args)
}
/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
(Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
(Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm),
(Closure(lb, lc, la, lm, lf, le, _), Closure(rb, rc, ra, rm, rf, re, _)) => {
- eq_closure_binder(lb, rb) && lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(le, re)
+ eq_closure_binder(lb, rb)
+ && lc == rc
+ && la.is_async() == ra.is_async()
+ && lm == rm
+ && eq_fn_decl(lf, rf)
+ && eq_expr(le, re)
},
(Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
(Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
pub fn eq_closure_binder(l: &ClosureBinder, r: &ClosureBinder) -> bool {
match (l, r) {
(ClosureBinder::NotPresent, ClosureBinder::NotPresent) => true,
- (ClosureBinder::For { generic_params: lp, .. }, ClosureBinder::For { generic_params: rp, .. }) =>
- lp.len() == rp.len() && std::iter::zip(lp.iter(), rp.iter()).all(|(l, r)| eq_generic_param(l, r)),
+ (ClosureBinder::For { generic_params: lp, .. }, ClosureBinder::For { generic_params: rp, .. }) => {
+ lp.len() == rp.len() && std::iter::zip(lp.iter(), rp.iter()).all(|(l, r)| eq_generic_param(l, r))
+ },
_ => false,
}
}
use Extern::*;
match (l, r) {
(None, None) | (Implicit(_), Implicit(_)) => true,
- (Explicit(l,_), Explicit(r,_)) => eq_str_lit(l, r),
+ (Explicit(l, _), Explicit(r, _)) => eq_str_lit(l, r),
_ => false,
}
}
use rustc_hir::def::Res;
use rustc_hir::HirIdMap;
use rustc_hir::{
- ArrayLen, BinOpKind, Closure, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId,
- InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt,
- StmtKind, Ty, TyKind, TypeBinding,
+ ArrayLen, BinOpKind, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard,
+ HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath,
+ Stmt, StmtKind, Ty, TyKind, TypeBinding,
};
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::LateContext;
impl HirEqInterExpr<'_, '_, '_> {
pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
match (&left.kind, &right.kind) {
- (&StmtKind::Local(l, ), &StmtKind::Local(r, )) => {
+ (&StmtKind::Local(l), &StmtKind::Local(r)) => {
// This additional check ensures that the type of the locals are equivalent even if the init
// expression or type have some inferred parts.
if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
std::mem::discriminant(&b.kind).hash(&mut self.s);
match &b.kind {
- StmtKind::Local(local, ) => {
+ StmtKind::Local(local) => {
self.hash_pat(local.pat);
if let Some(init) = local.init {
self.hash_expr(init);
ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
};
-use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture};
+use rustc_middle::ty::{
+ layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
+};
use rustc_middle::ty::{FloatTy, IntTy, UintTy};
use rustc_semver::RustcVersion;
use rustc_session::Session;
use rustc_target::abi::Integer;
use crate::consts::{constant, Constant};
-use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
+use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
use crate::visitors::expr_visitor_no_bodies;
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
Node::Expr(e) => match e.kind {
ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
- ExprKind::Assign(lhs, ..) | ExprKind::Assign(_, lhs, _) if lhs.hir_id == child_id => {
+ ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
return CaptureKind::Ref(Mutability::Mut);
},
ExprKind::Field(..) => {
captures: HirIdMap::default(),
};
v.visit_expr(expr);
- v.allow_closure.then(|| v.captures)
+ v.allow_closure.then_some(v.captures)
}
/// Returns the method names and argument list of nested method call expressions that make up
}
/// Gets the loop or closure enclosing the given expression, if any.
-pub fn get_enclosing_loop_or_closure<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
- for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
+pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &Expr<'_>,
+) -> Option<&'tcx Expr<'tcx>> {
+ for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
match node {
- Node::Expr(
- e @ Expr {
- kind: ExprKind::Loop(..) | ExprKind::Closure { .. },
- ..
+ Node::Expr(e) => match e.kind {
+ ExprKind::Closure { .. } => {
+ if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
+ && subs.as_closure().kind() == ClosureKind::FnOnce
+ {
+ continue;
+ }
+ let is_once = walk_to_expr_usage(cx, e, |node, id| {
+ let Node::Expr(e) = node else {
+ return None;
+ };
+ match e.kind {
+ ExprKind::Call(f, _) if f.hir_id == id => Some(()),
+ ExprKind::Call(f, args) => {
+ let i = args.iter().position(|arg| arg.hir_id == id)?;
+ let sig = expr_sig(cx, f)?;
+ let predicates = sig
+ .predicates_id()
+ .map_or(cx.param_env, |id| cx.tcx.param_env(id))
+ .caller_bounds();
+ sig.input(i).and_then(|ty| {
+ ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
+ })
+ },
+ ExprKind::MethodCall(_, args, _) => {
+ let i = args.iter().position(|arg| arg.hir_id == id)?;
+ let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
+ let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
+ ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
+ },
+ _ => None,
+ }
+ })
+ .is_some();
+ if !is_once {
+ return Some(e);
+ }
},
- ) => return Some(e),
- Node::Expr(_) | Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
+ ExprKind::Loop(..) => return Some(e),
+ _ => (),
+ },
+ Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
_ => break,
}
}
// names may refer to stabilized feature flags or library items
msrv_aliases! {
+ 1,62,0 { BOOL_THEN_SOME }
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN }
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"];
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
+pub const STR_FROM_UTF8_UNCHECKED: [&str; 4] = ["core", "str", "converts", "from_utf8_unchecked"];
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
#[cfg(feature = "internal")]
/// span containing `m!(0)`.
pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option<Span> {
let outer_span = hygiene::walk_chain(span, outer);
- (outer_span.ctxt() == outer).then(|| outer_span)
+ (outer_span.ctxt() == outer).then_some(outer_span)
}
/// Removes block comments from the given `Vec` of lines.
#![deny(clippy::missing_docs_in_private_items)]
use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite};
+use crate::ty::expr_sig;
use crate::{get_parent_expr_for_hir, higher};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{ast, token};
use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use std::borrow::Cow;
use std::fmt::{Display, Write as _};
-use std::iter;
use std::ops::{Add, Neg, Not, Sub};
/// A helper type to build suggestion correctly handling parentheses.
/// indicates whether the function from `parent_expr` takes its args by double reference
fn func_takes_arg_by_double_ref(&self, parent_expr: &'tcx hir::Expr<'_>, cmt_hir_id: HirId) -> bool {
- let (call_args, inputs) = match parent_expr.kind {
+ let ty = match parent_expr.kind {
ExprKind::MethodCall(_, call_args, _) => {
- if let Some(method_did) = self.cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) {
- (call_args, self.cx.tcx.fn_sig(method_did).skip_binder().inputs())
+ if let Some(sig) = self
+ .cx
+ .typeck_results()
+ .type_dependent_def_id(parent_expr.hir_id)
+ .map(|did| self.cx.tcx.fn_sig(did).skip_binder())
+ {
+ call_args
+ .iter()
+ .position(|arg| arg.hir_id == cmt_hir_id)
+ .map(|i| sig.inputs()[i])
} else {
return false;
}
},
ExprKind::Call(func, call_args) => {
- let typ = self.cx.typeck_results().expr_ty(func);
- (call_args, typ.fn_sig(self.cx.tcx).skip_binder().inputs())
+ if let Some(sig) = expr_sig(self.cx, func) {
+ call_args
+ .iter()
+ .position(|arg| arg.hir_id == cmt_hir_id)
+ .and_then(|i| sig.input(i))
+ .map(ty::Binder::skip_binder)
+ } else {
+ return false;
+ }
},
_ => return false,
};
- iter::zip(call_args, inputs)
- .any(|(arg, ty)| arg.hir_id == cmt_hir_id && matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
+ ty.map_or(false, |ty| matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
}
}
/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
/// * [Common tools for writing lints] for an example how to use this function and other options.
///
-/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
+/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
pub fn implements_trait<'tcx>(
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
/// A signature for a function like type.
#[derive(Clone, Copy)]
pub enum ExprFnSig<'tcx> {
- Sig(Binder<'tcx, FnSig<'tcx>>),
+ Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>),
Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>),
}
/// bounds only for variadic functions, otherwise this will panic.
pub fn input(self, i: usize) -> Option<Binder<'tcx, Ty<'tcx>>> {
match self {
- Self::Sig(sig) => {
+ Self::Sig(sig, _) => {
if sig.c_variadic() {
sig.inputs().map_bound(|inputs| inputs.get(i).copied()).transpose()
} else {
/// functions, otherwise this will panic.
pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> {
match self {
- Self::Sig(sig) => {
+ Self::Sig(sig, _) => {
if sig.c_variadic() {
sig.inputs()
.map_bound(|inputs| inputs.get(i).copied())
/// specified.
pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
match self {
- Self::Sig(sig) | Self::Closure(_, sig) => Some(sig.output()),
+ Self::Sig(sig, _) | Self::Closure(_, sig) => Some(sig.output()),
Self::Trait(_, output) => output,
}
}
+
+ pub fn predicates_id(&self) -> Option<DefId> {
+ if let ExprFnSig::Sig(_, id) = *self { id } else { None }
+ }
}
/// If the expression is function like, get the signature for it.
pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
- Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
+ Some(ExprFnSig::Sig(cx.tcx.fn_sig(id), Some(id)))
} else {
ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
}
}
fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
+ if ty.is_box() {
+ return ty_sig(cx, ty.boxed_ty());
+ }
match *ty.kind() {
ty::Closure(id, subs) => {
let decl = id
.and_then(|id| cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id)));
Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
},
- ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
- ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
+ ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))),
+ ty::Opaque(id, _) => ty_sig(cx, cx.tcx.type_of(id)),
+ ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
ty::Dynamic(bounds, _) => {
let lang_items = cx.tcx.lang_items();
match bounds.principal() {
_ => None,
}
}
+
+/// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
+pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [Predicate<'_>]) -> bool {
+ let ty::Param(ty) = *ty.kind() else {
+ return false;
+ };
+ let lang = tcx.lang_items();
+ let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id))
+ = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait())
+ else {
+ return false;
+ };
+ predicates
+ .iter()
+ .try_fold(false, |found, p| {
+ if let PredicateKind::Trait(p) = p.kind().skip_binder()
+ && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
+ && ty.index == self_ty.index
+ {
+ // This should use `super_traits_of`, but that's a private function.
+ if p.trait_ref.def_id == fn_once_id {
+ return Some(true);
+ } else if p.trait_ref.def_id == fn_mut_id || p.trait_ref.def_id == fn_id {
+ return None;
+ }
+ }
+ Some(found)
+ })
+ .unwrap_or(false)
+}
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
use rustc_hir::{
- Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, QPath, Stmt, UnOp,
+ Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, Stmt, UnOp,
UnsafeSource, Unsafety,
};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::adjustment::Adjust;
use rustc_middle::ty::{self, Ty, TypeckResults};
+use rustc_span::Span;
+
+mod internal {
+ /// Trait for visitor functions to control whether or not to descend to child nodes. Implemented
+ /// for only two types. `()` always descends. `Descend` allows controlled descent.
+ pub trait Continue {
+ fn descend(&self) -> bool;
+ }
+}
+use internal::Continue;
+
+impl Continue for () {
+ fn descend(&self) -> bool {
+ true
+ }
+}
+
+/// Allows for controlled descent when using visitor functions. Use `()` instead when always
+/// descending into child nodes.
+#[derive(Clone, Copy)]
+pub enum Descend {
+ Yes,
+ No,
+}
+impl From<bool> for Descend {
+ fn from(from: bool) -> Self {
+ if from { Self::Yes } else { Self::No }
+ }
+}
+impl Continue for Descend {
+ fn descend(&self) -> bool {
+ matches!(self, Self::Yes)
+ }
+}
+
+/// Calls the given function once for each expression contained. This does not enter any bodies or
+/// nested items.
+pub fn for_each_expr<'tcx, B, C: Continue>(
+ node: impl Visitable<'tcx>,
+ f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
+) -> Option<B> {
+ struct V<B, F> {
+ f: F,
+ res: Option<B>,
+ }
+ impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<B, F> {
+ fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
+ if self.res.is_some() {
+ return;
+ }
+ match (self.f)(e) {
+ ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
+ ControlFlow::Break(b) => self.res = Some(b),
+ ControlFlow::Continue(_) => (),
+ }
+ }
+
+ // Avoid unnecessary `walk_*` calls.
+ fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {}
+ fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
+ fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
+ // Avoid monomorphising all `visit_*` functions.
+ fn visit_nested_item(&mut self, _: ItemId) {}
+ }
+ let mut v = V { f, res: None };
+ node.visit(&mut v);
+ v.res
+}
/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
/// bodies (i.e. closures) are visited.
})
.is_break()
}
+
+/// Runs the given function for each path expression referencing the given local which occur after
+/// the given expression.
+pub fn for_each_local_assignment<'tcx, B>(
+ cx: &LateContext<'tcx>,
+ local_id: HirId,
+ f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
+) -> ControlFlow<B> {
+ struct V<'cx, 'tcx, F, B> {
+ cx: &'cx LateContext<'tcx>,
+ local_id: HirId,
+ res: ControlFlow<B>,
+ f: F,
+ }
+ impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
+ type NestedFilter = nested_filter::OnlyBodies;
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
+ fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
+ if let ExprKind::Assign(lhs, rhs, _) = e.kind
+ && self.res.is_continue()
+ && path_to_local_id(lhs, self.local_id)
+ {
+ self.res = (self.f)(rhs);
+ self.visit_expr(rhs);
+ } else {
+ walk_expr(self, e);
+ }
+ }
+ }
+
+ if let Some(b) = get_enclosing_block(cx, local_id) {
+ let mut v = V {
+ cx,
+ local_id,
+ res: ControlFlow::Continue(()),
+ f,
+ };
+ v.visit_block(b);
+ v.res
+ } else {
+ ControlFlow::Continue(())
+ }
+}
[package]
name = "lintcheck"
version = "0.0.1"
-description = "tool to monitor impact of changes in Clippys lints on a part of the ecosystem"
+description = "tool to monitor impact of changes in Clippy's lints on a part of the ecosystem"
readme = "README.md"
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-lang/rust-clippy"
[dependencies]
cargo_metadata = "0.14"
-clap = "3.1"
+clap = "3.2"
flate2 = "1.0"
rayon = "1.5.1"
serde = { version = "1.0", features = ["derive"] }
### Fix mode
You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `--fix` and
-print a warning if Clippys suggestions fail to apply (if the resulting code does not build).
+print a warning if Clippy's suggestions fail to apply (if the resulting code does not build).
This lets us spot bad suggestions or false positives automatically in some cases.
Please note that the target dir should be cleaned afterwards since clippy will modify
fn main() {
// assert that we launch lintcheck from the repo root (via cargo lintcheck)
if std::fs::metadata("lintcheck/Cargo.toml").is_err() {
- eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively.");
+ eprintln!("lintcheck needs to be run from clippy's repo root!\nUse `cargo lintcheck` alternatively.");
std::process::exit(3);
}
.map(|o| String::from_utf8_lossy(&o.stdout).into_owned())
.expect("could not get clippy version!");
- // download and extract the crates, then run clippy on them and collect clippys warnings
+ // download and extract the crates, then run clippy on them and collect clippy's warnings
// flatten into one big list of warnings
let crates = read_crates(&config.sources_toml_path);
[toolchain]
-channel = "nightly-2022-06-30"
+channel = "nightly-2022-07-15"
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
"single_component_path_imports_nested_first.rs",
"string_add.rs",
"toplevel_ref_arg_non_rustfix.rs",
+ "trait_duplication_in_bounds.rs",
"unit_arg.rs",
"unnecessary_clone.rs",
"unnecessary_lazy_eval_unfixable.rs",
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.arg("--all-targets")
- .arg("--all-features")
- .arg("--")
- .args(args)
- .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
+ .arg("--all-features");
+
+ if let Ok(dogfood_args) = std::env::var("__CLIPPY_DOGFOOD_ARGS") {
+ for arg in dogfood_args.split_whitespace() {
+ command.arg(arg);
+ }
+ }
+
+ command.arg("--").args(args);
+ command.arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
if cfg!(feature = "internal") {
// internal lints only exist if we build with the internal feature
+#![feature(once_cell)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]
use std::ffi::OsStr;
use std::path::PathBuf;
+use std::sync::LazyLock;
use regex::RegexSet;
impl Message {
fn new(path: PathBuf) -> Self {
- let content: String = std::fs::read_to_string(&path).unwrap();
// we don't want the first letter after "error: ", "help: " ... to be capitalized
// also no punctuation (except for "?" ?) at the end of a line
- let regex_set: RegexSet = RegexSet::new(&[
- r"error: [A-Z]",
- r"help: [A-Z]",
- r"warning: [A-Z]",
- r"note: [A-Z]",
- r"try this: [A-Z]",
- r"error: .*[.!]$",
- r"help: .*[.!]$",
- r"warning: .*[.!]$",
- r"note: .*[.!]$",
- r"try this: .*[.!]$",
- ])
- .unwrap();
+ static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {
+ RegexSet::new(&[
+ r"error: [A-Z]",
+ r"help: [A-Z]",
+ r"warning: [A-Z]",
+ r"note: [A-Z]",
+ r"try this: [A-Z]",
+ r"error: .*[.!]$",
+ r"help: .*[.!]$",
+ r"warning: .*[.!]$",
+ r"note: .*[.!]$",
+ r"try this: .*[.!]$",
+ ])
+ .unwrap()
+ });
// sometimes the first character is capitalized and it is legal (like in "C-like enum variants") or
// we want to ask a question ending in "?"
- let exceptions_set: RegexSet = RegexSet::new(&[
- r".*C-like enum variant discriminant is not portable to 32-bit targets",
- r".*did you mean `unix`?",
- r".*the arguments may be inverted...",
- r".*Intel x86 assembly syntax used",
- r".*AT&T x86 assembly syntax used",
- r".*remove .*the return type...",
- r"note: Clippy version: .*",
- r"the compiler unexpectedly panicked. this is a bug.",
- ])
- .unwrap();
+ static EXCEPTIONS_SET: LazyLock<RegexSet> = LazyLock::new(|| {
+ RegexSet::new(&[
+ r"\.\.\.$",
+ r".*C-like enum variant discriminant is not portable to 32-bit targets",
+ r".*Intel x86 assembly syntax used",
+ r".*AT&T x86 assembly syntax used",
+ r"note: Clippy version: .*",
+ r"the compiler unexpectedly panicked. this is a bug.",
+ ])
+ .unwrap()
+ });
+
+ let content: String = std::fs::read_to_string(&path).unwrap();
let bad_lines = content
.lines()
- .filter(|line| regex_set.matches(line).matched_any())
+ .filter(|line| REGEX_SET.matches(line).matched_any())
// ignore exceptions
- .filter(|line| !exceptions_set.matches(line).matched_any())
+ .filter(|line| !EXCEPTIONS_SET.matches(line).matched_any())
.map(ToOwned::to_owned)
.collect::<Vec<String>>();
-#[feature(lint_reasons)]
+#![feature(lint_reasons)]
mod a;
#[allow(clippy::duplicate_mod)]
mod d4;
-
fn main() {}
|
= help: replace all but one `mod` item with `use` items
+error: file is loaded as a module multiple times: `$DIR/d.rs`
+ --> $DIR/main.rs:18:1
+ |
+LL | mod d;
+ | ^^^^^^ first loaded here
+LL | / #[path = "d.rs"]
+LL | | mod d2;
+ | |_______^ loaded again here
+ |
+ = help: replace all but one `mod` item with `use` items
+
error: file is loaded as a module multiple times: `$DIR/from_other_module.rs`
--> $DIR/main.rs:15:1
|
|
= help: replace all but one `mod` item with `use` items
-error: file is loaded as a module multiple times: `$DIR/b.rs`
- --> $DIR/main.rs:18:1
- |
-LL | mod d;
- | ^^^^^^ first loaded here
-LL | / #[path = "d.rs"]
-LL | | mod d2;
- | |_______^ loaded again here
- |
- = help: replace all but one `mod` item with `use` items
-
error: aborting due to 4 previous errors
let _: i32 = ((value % 4) + 4) % 4;
};
}
+
+#[macro_export]
+macro_rules! equatable_if_let {
+ ($a:ident) => {{ if let 2 = $a {} }};
+}
unused
)]
-use std::collections::HashMap;
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
macro_rules! boxit {
($init:expr, $x:ty) => {
boxit!(Vec::new(), Vec<u8>);
}
-fn test(foo: Box<Vec<bool>>) {}
+fn test1(foo: Box<Vec<bool>>) {}
fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
// pass if #31 is fixed
fn test4(foo: Box<HashMap<String, String>>) {}
+fn test5(foo: Box<HashSet<i64>>) {}
+
+fn test6(foo: Box<VecDeque<i32>>) {}
+
+fn test7(foo: Box<LinkedList<i16>>) {}
+
+fn test8(foo: Box<BTreeMap<i8, String>>) {}
+
+fn test9(foo: Box<BTreeSet<u64>>) {}
+
+fn test10(foo: Box<BinaryHeap<u32>>) {}
+
fn test_local_not_linted() {
let _: Box<Vec<bool>>;
}
error: you seem to be trying to use `Box<Vec<..>>`. Consider using just `Vec<..>`
- --> $DIR/box_collection.rs:21:14
+ --> $DIR/box_collection.rs:21:15
|
-LL | fn test(foo: Box<Vec<bool>>) {}
- | ^^^^^^^^^^^^^^
+LL | fn test1(foo: Box<Vec<bool>>) {}
+ | ^^^^^^^^^^^^^^
|
= note: `-D clippy::box-collection` implied by `-D warnings`
= help: `Vec<..>` is already on the heap, `Box<Vec<..>>` makes an extra allocation
|
= help: `HashMap<..>` is already on the heap, `Box<HashMap<..>>` makes an extra allocation
-error: aborting due to 3 previous errors
+error: you seem to be trying to use `Box<HashSet<..>>`. Consider using just `HashSet<..>`
+ --> $DIR/box_collection.rs:32:15
+ |
+LL | fn test5(foo: Box<HashSet<i64>>) {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: `HashSet<..>` is already on the heap, `Box<HashSet<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<VecDeque<..>>`. Consider using just `VecDeque<..>`
+ --> $DIR/box_collection.rs:34:15
+ |
+LL | fn test6(foo: Box<VecDeque<i32>>) {}
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: `VecDeque<..>` is already on the heap, `Box<VecDeque<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<LinkedList<..>>`. Consider using just `LinkedList<..>`
+ --> $DIR/box_collection.rs:36:15
+ |
+LL | fn test7(foo: Box<LinkedList<i16>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: `LinkedList<..>` is already on the heap, `Box<LinkedList<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<BTreeMap<..>>`. Consider using just `BTreeMap<..>`
+ --> $DIR/box_collection.rs:38:15
+ |
+LL | fn test8(foo: Box<BTreeMap<i8, String>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: `BTreeMap<..>` is already on the heap, `Box<BTreeMap<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<BTreeSet<..>>`. Consider using just `BTreeSet<..>`
+ --> $DIR/box_collection.rs:40:15
+ |
+LL | fn test9(foo: Box<BTreeSet<u64>>) {}
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: `BTreeSet<..>` is already on the heap, `Box<BTreeSet<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<BinaryHeap<..>>`. Consider using just `BinaryHeap<..>`
+ --> $DIR/box_collection.rs:42:16
+ |
+LL | fn test10(foo: Box<BinaryHeap<u32>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: `BinaryHeap<..>` is already on the heap, `Box<BinaryHeap<..>>` makes an extra allocation
+
+error: aborting due to 9 previous errors
#![allow(dead_code)]
#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+use std::sync::Mutex;
+
// ##################################
// # Issue clippy#7369
// ##################################
let (y, x) = x;
foo(x, y)
};
+
+ let m = Mutex::new(0u32);
+ let l = m.lock().unwrap();
+ let _ = if true {
+ drop(l);
+ println!("foo");
+ m.lock().unwrap();
+ 0
+ } else if *l == 0 {
+ drop(l);
+ println!("foo");
+ println!("bar");
+ m.lock().unwrap();
+ 1
+ } else {
+ drop(l);
+ println!("foo");
+ println!("baz");
+ m.lock().unwrap();
+ 2
+ };
+
+ if true {
+ let _guard = m.lock();
+ println!("foo");
+ } else {
+ println!("foo");
+ }
+
+ if true {
+ let _guard = m.lock();
+ println!("foo");
+ println!("bar");
+ } else {
+ let _guard = m.lock();
+ println!("foo");
+ println!("baz");
+ }
+
+ let mut c = 0;
+ for _ in 0..5 {
+ if c == 0 {
+ c += 1;
+ println!("0");
+ } else if c == 1 {
+ c += 1;
+ println!("1");
+ } else {
+ c += 1;
+ println!("more");
+ }
+ }
}
|
= note: `-D clippy::unnecessary-cast` implied by `-D warnings`
-error: aborting due to 20 previous errors
+error: aborting due to 18 previous errors
--- /dev/null
+pub struct Thing;
+
+pub fn has_thing(things: &[Thing]) -> bool {
+ let is_thing_ready = |_peer: &Thing| -> bool { todo!() };
+ things.iter().find(|p| is_thing_ready(p)).is_some()
+}
+
+fn main() {}
--- /dev/null
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/ice-9041.rs:5:19
+ |
+LL | things.iter().find(|p| is_thing_ready(p)).is_some()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|p| is_thing_ready(&p))`
+ |
+ = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: aborting due to previous error
+
// run-rustfix
+// aux-build:macro_rules.rs
#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
#![warn(clippy::equatable_if_let)]
+#[macro_use]
+extern crate macro_rules;
+
use std::cmp::Ordering;
#[derive(PartialEq)]
if "abc" == m1!(x) {
println!("OK");
}
+
+ equatable_if_let!(a);
}
// run-rustfix
+// aux-build:macro_rules.rs
#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
#![warn(clippy::equatable_if_let)]
+#[macro_use]
+extern crate macro_rules;
+
use std::cmp::Ordering;
#[derive(PartialEq)]
if let m1!(x) = "abc" {
println!("OK");
}
+
+ equatable_if_let!(a);
}
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:49:8
+ --> $DIR/equatable_if_let.rs:53:8
|
LL | if let 2 = a {}
| ^^^^^^^^^ help: try: `a == 2`
= note: `-D clippy::equatable-if-let` implied by `-D warnings`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:50:8
+ --> $DIR/equatable_if_let.rs:54:8
|
LL | if let Ordering::Greater = a.cmp(&b) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:51:8
+ --> $DIR/equatable_if_let.rs:55:8
|
LL | if let Some(2) = c {}
| ^^^^^^^^^^^^^^^ help: try: `c == Some(2)`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:52:8
+ --> $DIR/equatable_if_let.rs:56:8
|
LL | if let Struct { a: 2, b: false } = d {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:53:8
+ --> $DIR/equatable_if_let.rs:57:8
|
LL | if let Enum::TupleVariant(32, 64) = e {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:54:8
+ --> $DIR/equatable_if_let.rs:58:8
|
LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:55:8
+ --> $DIR/equatable_if_let.rs:59:8
|
LL | if let Enum::UnitVariant = e {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:56:8
+ --> $DIR/equatable_if_let.rs:60:8
|
LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:66:8
+ --> $DIR/equatable_if_let.rs:70:8
|
LL | if let NotStructuralEq::A = g {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:68:8
+ --> $DIR/equatable_if_let.rs:72:8
|
LL | if let Some(NotStructuralEq::A) = Some(g) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:75:8
+ --> $DIR/equatable_if_let.rs:79:8
|
LL | if let m1!(x) = "abc" {
| ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)`
unsafe {
var(0, &**x);
}
+
+ let s = &"str";
+ let _ = || return *s;
+ let _ = || -> &'static str { return s };
}
unsafe {
var(0, &**x);
}
+
+ let s = &"str";
+ let _ = || return *s;
+ let _ = || -> &'static str { return *s };
}
LL | f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference
| ^^^^^^^^^^ help: try this: `ref_str`
-error: aborting due to 32 previous errors
+error: deref which would be done by auto-deref
+ --> $DIR/explicit_auto_deref.rs:217:41
+ |
+LL | let _ = || -> &'static str { return *s };
+ | ^^ help: try this: `s`
+
+error: aborting due to 33 previous errors
};
}
-// When mutexs are different don't warn
+// When mutexes are different don't warn
fn if_let_different_mutex() {
let m = Mutex::new(Some(0_u8));
let other = Mutex::new(None::<u8>);
// issue #7069.
new_foo!();
- // Shoule NOT lint because the order is the same as in the definition.
+ // Should NOT lint because the order is the same as in the definition.
Foo { x, y, z };
// Should NOT lint because z is not a shorthand init.
// issue #7069.
new_foo!();
- // Shoule NOT lint because the order is the same as in the definition.
+ // Should NOT lint because the order is the same as in the definition.
Foo { x, y, z };
// Should NOT lint because z is not a shorthand init.
--- /dev/null
+#![warn(clippy::invalid_utf8_in_unchecked)]
+
+fn main() {
+ // Valid
+ unsafe {
+ std::str::from_utf8_unchecked(&[99, 108, 105, 112, 112, 121]);
+ std::str::from_utf8_unchecked(&[b'c', b'l', b'i', b'p', b'p', b'y']);
+ std::str::from_utf8_unchecked(b"clippy");
+
+ let x = 0xA0;
+ std::str::from_utf8_unchecked(&[0xC0, x]);
+ }
+
+ // Invalid
+ unsafe {
+ std::str::from_utf8_unchecked(&[99, 108, 130, 105, 112, 112, 121]);
+ std::str::from_utf8_unchecked(&[b'c', b'l', b'\x82', b'i', b'p', b'p', b'y']);
+ std::str::from_utf8_unchecked(b"cl\x82ippy");
+ }
+}
--- /dev/null
+error: non UTF-8 literal in `std::str::from_utf8_unchecked`
+ --> $DIR/invalid_utf8_in_unchecked.rs:16:9
+ |
+LL | std::str::from_utf8_unchecked(&[99, 108, 130, 105, 112, 112, 121]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::invalid-utf8-in-unchecked` implied by `-D warnings`
+
+error: non UTF-8 literal in `std::str::from_utf8_unchecked`
+ --> $DIR/invalid_utf8_in_unchecked.rs:17:9
+ |
+LL | std::str::from_utf8_unchecked(&[b'c', b'l', b'/x82', b'i', b'p', b'p', b'y']);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: non UTF-8 literal in `std::str::from_utf8_unchecked`
+ --> $DIR/invalid_utf8_in_unchecked.rs:18:9
+ |
+LL | std::str::from_utf8_unchecked(b"cl/x82ippy");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
// run-rustfix
+#![feature(lint_reasons)]
#![warn(clippy::let_unit_value)]
-#![allow(clippy::no_effect)]
-#![allow(unused_variables)]
+#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)]
macro_rules! let_and_return {
($n:expr) => {{
fn f3<T>(x: T) -> T {
x
}
- fn f4<T>(mut x: Vec<T>) -> T {
- x.pop().unwrap()
+ fn f5<T: Default>(x: bool) -> Option<T> {
+ x.then(|| T::default())
}
let _: () = f(); // Ok
f3(()); // Lint
f3(()); // Lint
- f4(vec![()]); // Lint
- f4(vec![()]); // Lint
+ // Should lint:
+ // fn f4<T>(mut x: Vec<T>) -> T {
+ // x.pop().unwrap()
+ // }
+ // let _: () = f4(vec![()]);
+ // let x: () = f4(vec![()]);
// Ok
let _: () = {
Some(1) => f2(3),
Some(_) => (),
};
+
+ let _: () = f5(true).unwrap();
+
+ #[allow(clippy::let_unit_value)]
+ {
+ let x = f();
+ let y;
+ let z;
+ match 0 {
+ 0 => {
+ y = f();
+ z = f();
+ },
+ 1 => {
+ println!("test");
+ y = f();
+ z = f3(());
+ },
+ _ => panic!(),
+ }
+
+ let x1;
+ let x2;
+ if true {
+ x1 = f();
+ x2 = x1;
+ } else {
+ x2 = f();
+ x1 = x2;
+ }
+
+ let opt;
+ match f5(true) {
+ Some(x) => opt = x,
+ None => panic!(),
+ };
+
+ #[warn(clippy::let_unit_value)]
+ {
+ let _: () = x;
+ let _: () = y;
+ z;
+ let _: () = x1;
+ let _: () = x2;
+ let _: () = opt;
+ }
+ }
+
+ let () = f();
+}
+
+fn attributes() {
+ fn f() {}
+
+ #[allow(clippy::let_unit_value)]
+ let _ = f();
+ #[expect(clippy::let_unit_value)]
+ let _ = f();
}
// run-rustfix
+#![feature(lint_reasons)]
#![warn(clippy::let_unit_value)]
-#![allow(clippy::no_effect)]
-#![allow(unused_variables)]
+#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)]
macro_rules! let_and_return {
($n:expr) => {{
fn f3<T>(x: T) -> T {
x
}
- fn f4<T>(mut x: Vec<T>) -> T {
- x.pop().unwrap()
+ fn f5<T: Default>(x: bool) -> Option<T> {
+ x.then(|| T::default())
}
let _: () = f(); // Ok
let _: () = f3(()); // Lint
let x: () = f3(()); // Lint
- let _: () = f4(vec![()]); // Lint
- let x: () = f4(vec![()]); // Lint
+ // Should lint:
+ // fn f4<T>(mut x: Vec<T>) -> T {
+ // x.pop().unwrap()
+ // }
+ // let _: () = f4(vec![()]);
+ // let x: () = f4(vec![()]);
// Ok
let _: () = {
Some(1) => f2(3),
Some(_) => (),
};
+
+ let _: () = f5(true).unwrap();
+
+ #[allow(clippy::let_unit_value)]
+ {
+ let x = f();
+ let y;
+ let z;
+ match 0 {
+ 0 => {
+ y = f();
+ z = f();
+ },
+ 1 => {
+ println!("test");
+ y = f();
+ z = f3(());
+ },
+ _ => panic!(),
+ }
+
+ let x1;
+ let x2;
+ if true {
+ x1 = f();
+ x2 = x1;
+ } else {
+ x2 = f();
+ x1 = x2;
+ }
+
+ let opt;
+ match f5(true) {
+ Some(x) => opt = x,
+ None => panic!(),
+ };
+
+ #[warn(clippy::let_unit_value)]
+ {
+ let _: () = x;
+ let _: () = y;
+ let _: () = z;
+ let _: () = x1;
+ let _: () = x2;
+ let _: () = opt;
+ }
+ }
+
+ let () = f();
+}
+
+fn attributes() {
+ fn f() {}
+
+ #[allow(clippy::let_unit_value)]
+ let _ = f();
+ #[expect(clippy::let_unit_value)]
+ let _ = f();
}
| ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());`
error: this let-binding has unit value
- --> $DIR/let_unit.rs:88:5
- |
-LL | let _: () = f4(vec![()]); // Lint
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
-
-error: this let-binding has unit value
- --> $DIR/let_unit.rs:89:5
- |
-LL | let x: () = f4(vec![()]); // Lint
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
-
-error: this let-binding has unit value
- --> $DIR/let_unit.rs:98:5
+ --> $DIR/let_unit.rs:102:5
|
LL | let x: () = if true { f() } else { f2(0) }; // Lint
| ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| help: use a wild (`_`) binding: `_`
error: this let-binding has unit value
- --> $DIR/let_unit.rs:109:5
+ --> $DIR/let_unit.rs:113:5
|
LL | / let _: () = match Some(0) {
LL | | None => f2(1),
LL + };
|
-error: aborting due to 11 previous errors
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:160:13
+ |
+LL | let _: () = z;
+ | ^^^^^^^^^^^^^^ help: omit the `let` binding: `z;`
+
+error: aborting due to 10 previous errors
// is_ok(), unwrap_or()
let _ = (0..).filter_map(|a| to_res(a).ok());
+
+ let _ = (1..5)
+ .filter_map(|y| *to_ref(to_opt(y)));
+ let _ = (1..5)
+ .filter_map(|y| *to_ref(to_opt(y)));
+
+ let _ = (1..5)
+ .filter_map(|y| to_ref(to_res(y)).ok());
+ let _ = (1..5)
+ .filter_map(|y| to_ref(to_res(y)).ok());
+}
+
+#[rustfmt::skip]
+fn simple_equal() {
+ iter::<Option<&u8>>().find_map(|x| x.cloned());
+ iter::<&Option<&u8>>().find_map(|x| x.cloned());
+ iter::<&Option<String>>().find_map(|x| x.as_deref());
+ iter::<Option<&String>>().find_map(|y| to_ref(y).cloned());
+
+ iter::<Result<u8, ()>>().find_map(|x| x.ok());
+ iter::<&Result<u8, ()>>().find_map(|x| x.ok());
+ iter::<&&Result<u8, ()>>().find_map(|x| x.ok());
+ iter::<Result<&u8, ()>>().find_map(|x| x.cloned().ok());
+ iter::<&Result<&u8, ()>>().find_map(|x| x.cloned().ok());
+ iter::<&Result<String, ()>>().find_map(|x| x.as_deref().ok());
+ iter::<Result<&String, ()>>().find_map(|y| to_ref(y).cloned().ok());
}
fn no_lint() {
.map(|a| to_opt(a).unwrap());
}
+fn iter<T>() -> impl Iterator<Item = T> {
+ std::iter::empty()
+}
+
fn to_opt<T>(_: T) -> Option<T> {
unimplemented!()
}
unimplemented!()
}
+fn to_ref<'a, T>(_: T) -> &'a T {
+ unimplemented!()
+}
+
struct Issue8920<'a> {
option_field: Option<String>,
result_field: Result<String, ()>,
// is_ok(), unwrap_or()
let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
+
+ let _ = (1..5)
+ .filter(|&x| to_ref(to_opt(x)).is_some())
+ .map(|y| to_ref(to_opt(y)).unwrap());
+ let _ = (1..5)
+ .filter(|x| to_ref(to_opt(*x)).is_some())
+ .map(|y| to_ref(to_opt(y)).unwrap());
+
+ let _ = (1..5)
+ .filter(|&x| to_ref(to_res(x)).is_ok())
+ .map(|y| to_ref(to_res(y)).unwrap());
+ let _ = (1..5)
+ .filter(|x| to_ref(to_res(*x)).is_ok())
+ .map(|y| to_ref(to_res(y)).unwrap());
+}
+
+#[rustfmt::skip]
+fn simple_equal() {
+ iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
+ iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
+
+ iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
+ iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
}
fn no_lint() {
.map(|a| to_opt(a).unwrap());
}
+fn iter<T>() -> impl Iterator<Item = T> {
+ std::iter::empty()
+}
+
fn to_opt<T>(_: T) -> Option<T> {
unimplemented!()
}
unimplemented!()
}
+fn to_ref<'a, T>(_: T) -> &'a T {
+ unimplemented!()
+}
+
struct Issue8920<'a> {
option_field: Option<String>,
result_field: Result<String, ()>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:54:10
+ --> $DIR/manual_filter_map.rs:17:10
+ |
+LL | .filter(|&x| to_ref(to_opt(x)).is_some())
+ | __________^
+LL | | .map(|y| to_ref(to_opt(y)).unwrap());
+ | |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:20:10
+ |
+LL | .filter(|x| to_ref(to_opt(*x)).is_some())
+ | __________^
+LL | | .map(|y| to_ref(to_opt(y)).unwrap());
+ | |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:24:10
+ |
+LL | .filter(|&x| to_ref(to_res(x)).is_ok())
+ | __________^
+LL | | .map(|y| to_ref(to_res(y)).unwrap());
+ | |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:27:10
+ |
+LL | .filter(|x| to_ref(to_res(*x)).is_ok())
+ | __________^
+LL | | .map(|y| to_ref(to_res(y)).unwrap());
+ | |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:33:27
+ |
+LL | iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
+ |
+ = note: `-D clippy::manual-find-map` implied by `-D warnings`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:34:28
+ |
+LL | iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:35:31
+ |
+LL | iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:36:31
+ |
+LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:38:30
+ |
+LL | iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:39:31
+ |
+LL | iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:40:32
+ |
+LL | iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:41:31
+ |
+LL | iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:42:32
+ |
+LL | iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:43:35
+ |
+LL | iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_filter_map.rs:44:35
+ |
+LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+ --> $DIR/manual_filter_map.rs:92:10
|
LL | .filter(|f| f.option_field.is_some())
| __________^
| |_________________________________________________^ help: try: `filter_map(|f| f.option_field.clone())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:59:10
+ --> $DIR/manual_filter_map.rs:97:10
|
LL | .filter(|f| f.ref_field.is_some())
| __________^
| |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.cloned())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:64:10
+ --> $DIR/manual_filter_map.rs:102:10
|
LL | .filter(|f| f.ref_field.is_some())
| __________^
| |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.copied())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:69:10
+ --> $DIR/manual_filter_map.rs:107:10
|
LL | .filter(|f| f.result_field.is_ok())
| __________^
| |_________________________________________________^ help: try: `filter_map(|f| f.result_field.clone().ok())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:74:10
+ --> $DIR/manual_filter_map.rs:112:10
|
LL | .filter(|f| f.result_field.is_ok())
| __________^
| |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_ref().ok())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:79:10
+ --> $DIR/manual_filter_map.rs:117:10
|
LL | .filter(|f| f.result_field.is_ok())
| __________^
| |____________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref().ok())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:84:10
+ --> $DIR/manual_filter_map.rs:122:10
|
LL | .filter(|f| f.result_field.is_ok())
| __________^
| |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_mut().ok())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:89:10
+ --> $DIR/manual_filter_map.rs:127:10
|
LL | .filter(|f| f.result_field.is_ok())
| __________^
| |________________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref_mut().ok())`
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
- --> $DIR/manual_filter_map.rs:94:10
+ --> $DIR/manual_filter_map.rs:132:10
|
LL | .filter(|f| f.result_field.is_ok())
| __________^
LL | | .map(|f| f.result_field.to_owned().unwrap());
| |____________________________________________________^ help: try: `filter_map(|f| f.result_field.to_owned().ok())`
-error: aborting due to 12 previous errors
+error: aborting due to 27 previous errors
// is_ok(), unwrap_or()
let _ = (0..).find_map(|a| to_res(a).ok());
+
+ let _ = (1..5)
+ .find_map(|y| *to_ref(to_opt(y)));
+ let _ = (1..5)
+ .find_map(|y| *to_ref(to_opt(y)));
+
+ let _ = (1..5)
+ .find_map(|y| to_ref(to_res(y)).ok());
+ let _ = (1..5)
+ .find_map(|y| to_ref(to_res(y)).ok());
+}
+
+#[rustfmt::skip]
+fn simple_equal() {
+ iter::<Option<u8>>().find_map(|x| x);
+ iter::<&Option<u8>>().find_map(|x| *x);
+ iter::<&&Option<u8>>().find_map(|x| **x);
+ iter::<Option<&u8>>().find_map(|x| x.cloned());
+ iter::<&Option<&u8>>().find_map(|x| x.cloned());
+ iter::<&Option<String>>().find_map(|x| x.as_deref());
+ iter::<Option<&String>>().find_map(|y| to_ref(y).cloned());
+
+ iter::<Result<u8, ()>>().find_map(|x| x.ok());
+ iter::<&Result<u8, ()>>().find_map(|x| x.ok());
+ iter::<&&Result<u8, ()>>().find_map(|x| x.ok());
+ iter::<Result<&u8, ()>>().find_map(|x| x.cloned().ok());
+ iter::<&Result<&u8, ()>>().find_map(|x| x.cloned().ok());
+ iter::<&Result<String, ()>>().find_map(|x| x.as_deref().ok());
+ iter::<Result<&String, ()>>().find_map(|y| to_ref(y).cloned().ok());
}
fn no_lint() {
.map(|a| to_opt(a).unwrap());
}
+fn iter<T>() -> impl Iterator<Item = T> {
+ std::iter::empty()
+}
+
fn to_opt<T>(_: T) -> Option<T> {
unimplemented!()
}
unimplemented!()
}
+fn to_ref<'a, T>(_: T) -> &'a T {
+ unimplemented!()
+}
+
struct Issue8920<'a> {
option_field: Option<String>,
result_field: Result<String, ()>,
// is_ok(), unwrap_or()
let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
+
+ let _ = (1..5)
+ .find(|&x| to_ref(to_opt(x)).is_some())
+ .map(|y| to_ref(to_opt(y)).unwrap());
+ let _ = (1..5)
+ .find(|x| to_ref(to_opt(*x)).is_some())
+ .map(|y| to_ref(to_opt(y)).unwrap());
+
+ let _ = (1..5)
+ .find(|&x| to_ref(to_res(x)).is_ok())
+ .map(|y| to_ref(to_res(y)).unwrap());
+ let _ = (1..5)
+ .find(|x| to_ref(to_res(*x)).is_ok())
+ .map(|y| to_ref(to_res(y)).unwrap());
+}
+
+#[rustfmt::skip]
+fn simple_equal() {
+ iter::<Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+ iter::<&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+ iter::<&&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+ iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
+ iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
+
+ iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
+ iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
}
fn no_lint() {
.map(|a| to_opt(a).unwrap());
}
+fn iter<T>() -> impl Iterator<Item = T> {
+ std::iter::empty()
+}
+
fn to_opt<T>(_: T) -> Option<T> {
unimplemented!()
}
unimplemented!()
}
+fn to_ref<'a, T>(_: T) -> &'a T {
+ unimplemented!()
+}
+
struct Issue8920<'a> {
option_field: Option<String>,
result_field: Result<String, ()>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:54:10
+ --> $DIR/manual_find_map.rs:17:10
+ |
+LL | .find(|&x| to_ref(to_opt(x)).is_some())
+ | __________^
+LL | | .map(|y| to_ref(to_opt(y)).unwrap());
+ | |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:20:10
+ |
+LL | .find(|x| to_ref(to_opt(*x)).is_some())
+ | __________^
+LL | | .map(|y| to_ref(to_opt(y)).unwrap());
+ | |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:24:10
+ |
+LL | .find(|&x| to_ref(to_res(x)).is_ok())
+ | __________^
+LL | | .map(|y| to_ref(to_res(y)).unwrap());
+ | |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:27:10
+ |
+LL | .find(|x| to_ref(to_res(*x)).is_ok())
+ | __________^
+LL | | .map(|y| to_ref(to_res(y)).unwrap());
+ | |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:33:26
+ |
+LL | iter::<Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x)`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:34:27
+ |
+LL | iter::<&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| *x)`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:35:28
+ |
+LL | iter::<&&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| **x)`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:36:27
+ |
+LL | iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:37:28
+ |
+LL | iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:38:31
+ |
+LL | iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:39:31
+ |
+LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:41:30
+ |
+LL | iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:42:31
+ |
+LL | iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:43:32
+ |
+LL | iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:44:31
+ |
+LL | iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:45:32
+ |
+LL | iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:46:35
+ |
+LL | iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:47:35
+ |
+LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+ --> $DIR/manual_find_map.rs:95:10
|
LL | .find(|f| f.option_field.is_some())
| __________^
| |_________________________________________________^ help: try: `find_map(|f| f.option_field.clone())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:59:10
+ --> $DIR/manual_find_map.rs:100:10
|
LL | .find(|f| f.ref_field.is_some())
| __________^
| |_______________________________________________^ help: try: `find_map(|f| f.ref_field.cloned())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:64:10
+ --> $DIR/manual_find_map.rs:105:10
|
LL | .find(|f| f.ref_field.is_some())
| __________^
| |_______________________________________________^ help: try: `find_map(|f| f.ref_field.copied())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:69:10
+ --> $DIR/manual_find_map.rs:110:10
|
LL | .find(|f| f.result_field.is_ok())
| __________^
| |_________________________________________________^ help: try: `find_map(|f| f.result_field.clone().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:74:10
+ --> $DIR/manual_find_map.rs:115:10
|
LL | .find(|f| f.result_field.is_ok())
| __________^
| |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_ref().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:79:10
+ --> $DIR/manual_find_map.rs:120:10
|
LL | .find(|f| f.result_field.is_ok())
| __________^
| |____________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:84:10
+ --> $DIR/manual_find_map.rs:125:10
|
LL | .find(|f| f.result_field.is_ok())
| __________^
| |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_mut().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:89:10
+ --> $DIR/manual_find_map.rs:130:10
|
LL | .find(|f| f.result_field.is_ok())
| __________^
| |________________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref_mut().ok())`
error: `find(..).map(..)` can be simplified as `find_map(..)`
- --> $DIR/manual_find_map.rs:94:10
+ --> $DIR/manual_find_map.rs:135:10
|
LL | .find(|f| f.result_field.is_ok())
| __________^
LL | | .map(|f| f.result_field.to_owned().unwrap());
| |____________________________________________________^ help: try: `find_map(|f| f.result_field.to_owned().ok())`
-error: aborting due to 12 previous errors
+error: aborting due to 30 previous errors
for n in vec![Some(1), Some(2), Some(3)].iter().flatten() {
println!("{}", n);
}
+
+ run_unformatted_tests();
+}
+
+#[rustfmt::skip]
+fn run_unformatted_tests() {
+ // Skip rustfmt here on purpose so the suggestion does not fit in one line
+ for n in vec![
+ Some(1),
+ Some(2),
+ Some(3)
+ ].iter() {
+ if let Some(n) = n {
+ println!("{:?}", n);
+ }
+ }
}
LL | | }
| |_________^
-error: aborting due to 8 previous errors
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:116:5
+ |
+LL | / for n in vec![
+LL | | Some(1),
+LL | | Some(2),
+LL | | Some(3)
+... |
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: remove the `if let` statement in the for loop and then...
+ --> $DIR/manual_flatten.rs:121:9
+ |
+LL | / if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+ | |_________^
+help: try
+ |
+LL ~ for n in vec![
+LL + Some(1),
+LL + Some(2),
+LL + Some(3)
+LL ~ ].iter().flatten() {
+ |
+
+error: aborting due to 9 previous errors
}
fn issue8734() {
- // let _ = [0u8, 1, 2, 3]
- // .into_iter()
- // .map(|n| match n {
- // 1 => [n
- // .saturating_add(1)
- // .saturating_add(1)
- // .saturating_add(1)
- // .saturating_add(1)
- // .saturating_add(1)
- // .saturating_add(1)
- // .saturating_add(1)
- // .saturating_add(1)],
- // n => [n],
- // })
- // .flatten();
+ let _ = [0u8, 1, 2, 3]
+ .into_iter()
+ .flat_map(|n| match n {
+ 1 => [n
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)],
+ n => [n],
+ });
}
#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again
.and_then(|_| {
// we need some newlines
// so that the span is big enough
-// for a splitted output of the diagnostic
+// for a split output of the diagnostic
Some("")
// whitespace beforehand is important as well
});
}
fn issue8734() {
- // let _ = [0u8, 1, 2, 3]
- // .into_iter()
- // .map(|n| match n {
- // 1 => [n
- // .saturating_add(1)
- // .saturating_add(1)
- // .saturating_add(1)
- // .saturating_add(1)
- // .saturating_add(1)
- // .saturating_add(1)
- // .saturating_add(1)
- // .saturating_add(1)],
- // n => [n],
- // })
- // .flatten();
+ let _ = [0u8, 1, 2, 3]
+ .into_iter()
+ .map(|n| match n {
+ 1 => [n
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)
+ .saturating_add(1)],
+ n => [n],
+ })
+ .flatten();
}
#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again
.map(|_| {
// we need some newlines
// so that the span is big enough
-// for a splitted output of the diagnostic
+// for a split output of the diagnostic
Some("")
// whitespace beforehand is important as well
})
LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
| ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)`
+error: called `map(..).flatten()` on `Iterator`
+ --> $DIR/map_flatten_fixable.rs:39:10
+ |
+LL | .map(|n| match n {
+ | __________^
+LL | | 1 => [n
+LL | | .saturating_add(1)
+LL | | .saturating_add(1)
+... |
+LL | | })
+LL | | .flatten();
+ | |__________________^
+ |
+help: try replacing `map` with `flat_map` and remove the `.flatten()`
+ |
+LL ~ .flat_map(|n| match n {
+LL + 1 => [n
+LL + .saturating_add(1)
+LL + .saturating_add(1)
+LL + .saturating_add(1)
+LL + .saturating_add(1)
+LL + .saturating_add(1)
+LL + .saturating_add(1)
+LL + .saturating_add(1)
+LL + .saturating_add(1)],
+LL + n => [n],
+LL ~ });
+ |
+
error: called `map(..).flatten()` on `Option`
--> $DIR/map_flatten_fixable.rs:59:10
|
| __________^
LL | | // we need some newlines
LL | | // so that the span is big enough
-LL | | // for a splitted output of the diagnostic
+LL | | // for a split output of the diagnostic
... |
LL | | })
LL | | .flatten();
LL ~ .and_then(|_| {
LL + // we need some newlines
LL + // so that the span is big enough
-LL + // for a splitted output of the diagnostic
+LL + // for a split output of the diagnostic
LL + Some("")
LL + // whitespace beforehand is important as well
LL ~ });
|
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
// lint
let _ans = matches!(x, E::A(_) | E::B(_));
}
+ {
+ // lint
+ // skip rustfmt to prevent removing block for first pattern
+ #[rustfmt::skip]
+ let _ans = matches!(x, E::A(_) | E::B(_));
+ }
{
// lint
let _ans = !matches!(x, E::B(_) | E::C);
_ => false,
};
}
+ {
+ // lint
+ // skip rustfmt to prevent removing block for first pattern
+ #[rustfmt::skip]
+ let _ans = match x {
+ E::A(_) => {
+ true
+ }
+ E::B(_) => true,
+ _ => false,
+ };
+ }
{
// lint
let _ans = match x {
| |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:66:20
+ --> $DIR/match_expr_like_matches_macro.rs:68:20
+ |
+LL | let _ans = match x {
+ | ____________________^
+LL | | E::A(_) => {
+LL | | true
+LL | | }
+LL | | E::B(_) => true,
+LL | | _ => false,
+LL | | };
+ | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:78:20
|
LL | let _ans = match x {
| ____________________^
| |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:126:18
+ --> $DIR/match_expr_like_matches_macro.rs:138:18
|
LL | let _z = match &z {
| __________________^
| |_________^ help: try this: `matches!(z, Some(3))`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:135:18
+ --> $DIR/match_expr_like_matches_macro.rs:147:18
|
LL | let _z = match &z {
| __________________^
| |_________^ help: try this: `matches!(&z, Some(3))`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:152:21
+ --> $DIR/match_expr_like_matches_macro.rs:164:21
|
LL | let _ = match &z {
| _____________________^
| |_____________^ help: try this: `matches!(&z, AnEnum::X)`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:166:20
+ --> $DIR/match_expr_like_matches_macro.rs:178:20
|
LL | let _res = match &val {
| ____________________^
| |_________^ help: try this: `matches!(&val, &Some(ref _a))`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:178:20
+ --> $DIR/match_expr_like_matches_macro.rs:190:20
|
LL | let _res = match &val {
| ____________________^
LL | | };
| |_________^ help: try this: `matches!(&val, &Some(ref _a))`
-error: aborting due to 12 previous errors
+error: aborting due to 13 previous errors
fn foo_ref(&self) {}
}
(&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
+
+ struct S;
+ impl From<S> for u32 {
+ fn from(s: S) -> Self {
+ (&s).into()
+ }
+ }
+ impl From<&S> for u32 {
+ fn from(s: &S) -> Self {
+ 0
+ }
+ }
}
#[allow(clippy::needless_borrowed_reference)]
fn foo_ref(&self) {}
}
(&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
+
+ struct S;
+ impl From<S> for u32 {
+ fn from(s: S) -> Self {
+ (&s).into()
+ }
+ }
+ impl From<&S> for u32 {
+ fn from(s: &S) -> Self {
+ 0
+ }
+ }
}
#[allow(clippy::needless_borrowed_reference)]
let _: Result<i32, i32> = x;
let _: Result<i32, i32> = x;
// Input type mismatch, don't trigger
+ #[allow(clippy::question_mark)]
let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
}
let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
// Input type mismatch, don't trigger
+ #[allow(clippy::question_mark)]
let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
error: this if-let expression is unnecessary
- --> $DIR/needless_match.rs:129:21
+ --> $DIR/needless_match.rs:130:21
|
LL | let _: Simple = if let Simple::A = x {
| _____________________^
| |_____^ help: replace it with: `x`
error: this match expression is unnecessary
- --> $DIR/needless_match.rs:168:26
+ --> $DIR/needless_match.rs:169:26
|
LL | let _: Complex = match ce {
| __________________________^
}
fn main() {}
+
+pub struct IgnoreConstGenericNew(usize);
+impl IgnoreConstGenericNew {
+ pub fn new<const N: usize>() -> Self {
+ Self(N)
+ }
+}
+
+pub struct IgnoreLifetimeNew;
+impl IgnoreLifetimeNew {
+ pub fn new<'a>() -> Self {
+ Self
+ }
+}
#![allow(clippy::deref_addrof)]
#![allow(clippy::redundant_field_names)]
-
struct Unit;
struct Tuple(i32);
struct Struct {
error: statement with no effect
- --> $DIR/no_effect.rs:95:5
+ --> $DIR/no_effect.rs:94:5
|
LL | 0;
| ^^
= note: `-D clippy::no-effect` implied by `-D warnings`
error: statement with no effect
- --> $DIR/no_effect.rs:96:5
+ --> $DIR/no_effect.rs:95:5
|
LL | s2;
| ^^^
error: statement with no effect
- --> $DIR/no_effect.rs:97:5
+ --> $DIR/no_effect.rs:96:5
|
LL | Unit;
| ^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:98:5
+ --> $DIR/no_effect.rs:97:5
|
LL | Tuple(0);
| ^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:99:5
+ --> $DIR/no_effect.rs:98:5
|
LL | Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:100:5
+ --> $DIR/no_effect.rs:99:5
|
LL | Struct { ..s };
| ^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:101:5
+ --> $DIR/no_effect.rs:100:5
|
LL | Union { a: 0 };
| ^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:102:5
+ --> $DIR/no_effect.rs:101:5
|
LL | Enum::Tuple(0);
| ^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:103:5
+ --> $DIR/no_effect.rs:102:5
|
LL | Enum::Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:104:5
+ --> $DIR/no_effect.rs:103:5
|
LL | 5 + 6;
| ^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:105:5
+ --> $DIR/no_effect.rs:104:5
|
LL | *&42;
| ^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:106:5
+ --> $DIR/no_effect.rs:105:5
|
LL | &6;
| ^^^
error: statement with no effect
- --> $DIR/no_effect.rs:107:5
+ --> $DIR/no_effect.rs:106:5
|
LL | (5, 6, 7);
| ^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:108:5
+ --> $DIR/no_effect.rs:107:5
|
LL | box 42;
| ^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:109:5
+ --> $DIR/no_effect.rs:108:5
|
LL | ..;
| ^^^
error: statement with no effect
- --> $DIR/no_effect.rs:110:5
+ --> $DIR/no_effect.rs:109:5
|
LL | 5..;
| ^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:111:5
+ --> $DIR/no_effect.rs:110:5
|
LL | ..5;
| ^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:112:5
+ --> $DIR/no_effect.rs:111:5
|
LL | 5..6;
| ^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:113:5
+ --> $DIR/no_effect.rs:112:5
|
LL | 5..=6;
| ^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:114:5
+ --> $DIR/no_effect.rs:113:5
|
LL | [42, 55];
| ^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:115:5
+ --> $DIR/no_effect.rs:114:5
|
LL | [42, 55][1];
| ^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:116:5
+ --> $DIR/no_effect.rs:115:5
|
LL | (42, 55).1;
| ^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:117:5
+ --> $DIR/no_effect.rs:116:5
|
LL | [42; 55];
| ^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:118:5
+ --> $DIR/no_effect.rs:117:5
|
LL | [42; 55][13];
| ^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:120:5
+ --> $DIR/no_effect.rs:119:5
|
LL | || x += 5;
| ^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:122:5
+ --> $DIR/no_effect.rs:121:5
|
LL | FooString { s: s };
| ^^^^^^^^^^^^^^^^^^^
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:123:5
+ --> $DIR/no_effect.rs:122:5
|
LL | let _unused = 1;
| ^^^^^^^^^^^^^^^^
= note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:124:5
+ --> $DIR/no_effect.rs:123:5
|
LL | let _penguin = || println!("Some helpful closure");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:125:5
+ --> $DIR/no_effect.rs:124:5
|
LL | let _duck = Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:126:5
+ --> $DIR/no_effect.rs:125:5
|
LL | let _cat = [2, 4, 6, 8][2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
.reduce(|mut acc, f| {
acc.push_str(&f);
acc
- }).unwrap_or_default();
+ })
+ .unwrap_or_default();
}
fn more_to_max_suggestion_highest_lines_1() {
let _ = "";
acc.push_str(&f);
acc
- }).unwrap_or_default();
+ })
+ .unwrap_or_default();
}
fn equal_to_max_suggestion_highest_lines() {
let _ = "";
acc.push_str(&f);
acc
- }).unwrap_or_default();
+ })
+ .unwrap_or_default();
}
fn less_than_max_suggestion_highest_lines() {
map.reduce(|mut acc, f| {
acc.push_str(&f);
acc
- }).unwrap_or_default();
+ })
+ .unwrap_or_default();
}
}
= note: `-D clippy::or-fun-call` implied by `-D warnings`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:52:5
+ --> $DIR/or_fun_call.rs:52:14
|
LL | with_new.unwrap_or(Vec::new());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_new.unwrap_or_default()`
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a function call
--> $DIR/or_fun_call.rs:55:21
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))`
error: use of `unwrap_or` followed by a call to `default`
- --> $DIR/or_fun_call.rs:64:5
+ --> $DIR/or_fun_call.rs:64:24
|
LL | with_default_trait.unwrap_or(Default::default());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_trait.unwrap_or_default()`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a call to `default`
- --> $DIR/or_fun_call.rs:67:5
+ --> $DIR/or_fun_call.rs:67:23
|
LL | with_default_type.unwrap_or(u64::default());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_type.unwrap_or_default()`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a function call
--> $DIR/or_fun_call.rs:70:18
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(<FakeDefault>::default)`
error: use of `unwrap_or` followed by a call to `default`
- --> $DIR/or_fun_call.rs:73:5
+ --> $DIR/or_fun_call.rs:73:18
|
LL | real_default.unwrap_or(<FakeDefault as Default>::default());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `real_default.unwrap_or_default()`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:76:5
+ --> $DIR/or_fun_call.rs:76:14
|
LL | with_vec.unwrap_or(vec![]);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_vec.unwrap_or_default()`
+ | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a function call
--> $DIR/or_fun_call.rs:79:21
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:182:9
- |
-LL | / frames
-LL | | .iter()
-LL | | .map(|f: &String| f.to_lowercase())
-LL | | .reduce(|mut acc, f| {
-... |
-LL | | })
-LL | | .unwrap_or(String::new());
- | |_____________________________________^
- |
-help: try this
- |
-LL ~ frames
-LL + .iter()
-LL + .map(|f: &String| f.to_lowercase())
-LL + .reduce(|mut acc, f| {
-LL + acc.push_str(&f);
-LL + acc
-LL ~ }).unwrap_or_default();
+ --> $DIR/or_fun_call.rs:189:14
|
+LL | .unwrap_or(String::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:195:9
- |
-LL | / iter.map(|f: &String| f.to_lowercase())
-LL | | .reduce(|mut acc, f| {
-LL | | let _ = "";
-LL | | let _ = "";
-... |
-LL | | })
-LL | | .unwrap_or(String::new());
- | |_____________________________________^
- |
-help: try this
- |
-LL ~ iter.map(|f: &String| f.to_lowercase())
-LL + .reduce(|mut acc, f| {
-LL + let _ = "";
-LL + let _ = "";
-LL + acc.push_str(&f);
-LL + acc
-LL ~ }).unwrap_or_default();
+ --> $DIR/or_fun_call.rs:202:14
|
+LL | .unwrap_or(String::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:208:9
- |
-LL | / iter.map(|f: &String| f.to_lowercase())
-LL | | .reduce(|mut acc, f| {
-LL | | let _ = "";
-LL | | acc.push_str(&f);
-LL | | acc
-LL | | })
-LL | | .unwrap_or(String::new());
- | |_____________________________________^
- |
-help: try this
- |
-LL ~ iter.map(|f: &String| f.to_lowercase())
-LL + .reduce(|mut acc, f| {
-LL + let _ = "";
-LL + acc.push_str(&f);
-LL + acc
-LL ~ }).unwrap_or_default();
+ --> $DIR/or_fun_call.rs:214:14
|
+LL | .unwrap_or(String::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: use of `unwrap_or` followed by a call to `new`
- --> $DIR/or_fun_call.rs:221:9
- |
-LL | / map.reduce(|mut acc, f| {
-LL | | acc.push_str(&f);
-LL | | acc
-LL | | })
-LL | | .unwrap_or(String::new());
- | |_________________________________^
- |
-help: try this
- |
-LL ~ map.reduce(|mut acc, f| {
-LL + acc.push_str(&f);
-LL + acc
-LL ~ }).unwrap_or_default();
+ --> $DIR/or_fun_call.rs:225:10
|
+LL | .unwrap_or(String::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
error: aborting due to 22 previous errors
// run-rustfix
#![allow(unreachable_code)]
+#![allow(dead_code)]
#![allow(clippy::unnecessary_wraps)]
fn some_func(a: Option<u32>) -> Option<u32> {
NotOption::First
}
-fn main() {
- some_func(Some(42));
- some_func(None);
- some_other_func(Some(42));
+fn do_something() {}
- let copy_struct = CopyStruct { opt: Some(54) };
- copy_struct.func();
+fn err_immediate_return() -> Result<i32, i32> {
+ func_returning_result()?;
+ Ok(1)
+}
- let move_struct = MoveStruct {
- opt: Some(vec![42, 1337]),
- };
- move_struct.ref_func();
- move_struct.clone().mov_func_reuse();
- move_struct.mov_func_no_use();
+fn err_immediate_return_and_do_something() -> Result<i32, i32> {
+ func_returning_result()?;
+ do_something();
+ Ok(1)
+}
- let so = SeemsOption::Some(45);
- returns_something_similar_to_option(so);
+// No warning
+fn no_immediate_return() -> Result<i32, i32> {
+ if let Err(err) = func_returning_result() {
+ do_something();
+ return Err(err);
+ }
+ Ok(1)
+}
- func();
+// No warning
+fn mixed_result_and_option() -> Option<i32> {
+ if let Err(err) = func_returning_result() {
+ return Some(err);
+ }
+ None
+}
+
+// No warning
+fn else_if_check() -> Result<i32, i32> {
+ if true {
+ Ok(1)
+ } else if let Err(e) = func_returning_result() {
+ Err(e)
+ } else {
+ Err(-1)
+ }
+}
- let _ = result_func(Ok(42));
- let _ = f();
+// No warning
+#[allow(clippy::manual_map)]
+#[rustfmt::skip]
+fn option_map() -> Option<bool> {
+ if let Some(a) = Some(false) {
+ Some(!a)
+ } else {
+ None
+ }
}
+
+fn main() {}
// run-rustfix
#![allow(unreachable_code)]
+#![allow(dead_code)]
#![allow(clippy::unnecessary_wraps)]
fn some_func(a: Option<u32>) -> Option<u32> {
NotOption::First
}
-fn main() {
- some_func(Some(42));
- some_func(None);
- some_other_func(Some(42));
+fn do_something() {}
- let copy_struct = CopyStruct { opt: Some(54) };
- copy_struct.func();
+fn err_immediate_return() -> Result<i32, i32> {
+ if let Err(err) = func_returning_result() {
+ return Err(err);
+ }
+ Ok(1)
+}
- let move_struct = MoveStruct {
- opt: Some(vec![42, 1337]),
- };
- move_struct.ref_func();
- move_struct.clone().mov_func_reuse();
- move_struct.mov_func_no_use();
+fn err_immediate_return_and_do_something() -> Result<i32, i32> {
+ if let Err(err) = func_returning_result() {
+ return Err(err);
+ }
+ do_something();
+ Ok(1)
+}
- let so = SeemsOption::Some(45);
- returns_something_similar_to_option(so);
+// No warning
+fn no_immediate_return() -> Result<i32, i32> {
+ if let Err(err) = func_returning_result() {
+ do_something();
+ return Err(err);
+ }
+ Ok(1)
+}
- func();
+// No warning
+fn mixed_result_and_option() -> Option<i32> {
+ if let Err(err) = func_returning_result() {
+ return Some(err);
+ }
+ None
+}
- let _ = result_func(Ok(42));
- let _ = f();
+// No warning
+fn else_if_check() -> Result<i32, i32> {
+ if true {
+ Ok(1)
+ } else if let Err(e) = func_returning_result() {
+ Err(e)
+ } else {
+ Err(-1)
+ }
}
+
+// No warning
+#[allow(clippy::manual_map)]
+#[rustfmt::skip]
+fn option_map() -> Option<bool> {
+ if let Some(a) = Some(false) {
+ Some(!a)
+ } else {
+ None
+ }
+}
+
+fn main() {}
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:6:5
+ --> $DIR/question_mark.rs:7:5
|
LL | / if a.is_none() {
LL | | return None;
= note: `-D clippy::question-mark` implied by `-D warnings`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:51:9
+ --> $DIR/question_mark.rs:52:9
|
LL | / if (self.opt).is_none() {
LL | | return None;
| |_________^ help: replace it with: `(self.opt)?;`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:55:9
+ --> $DIR/question_mark.rs:56:9
|
LL | / if self.opt.is_none() {
LL | | return None
| |_________^ help: replace it with: `self.opt?;`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:59:17
+ --> $DIR/question_mark.rs:60:17
|
LL | let _ = if self.opt.is_none() {
| _________________^
LL | | };
| |_________^ help: replace it with: `Some(self.opt?)`
-error: this if-let-else may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:65:17
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:66:17
|
LL | let _ = if let Some(x) = self.opt {
| _________________^
| |_________^ help: replace it with: `self.opt?`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:82:9
+ --> $DIR/question_mark.rs:83:9
|
LL | / if self.opt.is_none() {
LL | | return None;
| |_________^ help: replace it with: `self.opt.as_ref()?;`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:90:9
+ --> $DIR/question_mark.rs:91:9
|
LL | / if self.opt.is_none() {
LL | | return None;
| |_________^ help: replace it with: `self.opt.as_ref()?;`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:98:9
+ --> $DIR/question_mark.rs:99:9
|
LL | / if self.opt.is_none() {
LL | | return None;
LL | | }
| |_________^ help: replace it with: `self.opt.as_ref()?;`
-error: this if-let-else may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:105:26
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:106:26
|
LL | let v: &Vec<_> = if let Some(ref v) = self.opt {
| __________________________^
LL | | };
| |_________^ help: replace it with: `self.opt.as_ref()?`
-error: this if-let-else may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:115:17
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:116:17
|
LL | let v = if let Some(v) = self.opt {
| _________________^
| |_________^ help: replace it with: `self.opt?`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:130:5
+ --> $DIR/question_mark.rs:131:5
|
LL | / if f().is_none() {
LL | | return None;
LL | | }
| |_____^ help: replace it with: `f()?;`
-error: this if-let-else may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:142:13
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:143:13
|
LL | let _ = if let Ok(x) = x { x } else { return x };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?`
error: this block may be rewritten with the `?` operator
- --> $DIR/question_mark.rs:144:5
+ --> $DIR/question_mark.rs:145:5
|
LL | / if x.is_err() {
LL | | return x;
LL | | }
| |_____^ help: replace it with: `x?;`
-error: aborting due to 13 previous errors
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:193:5
+ |
+LL | / if let Err(err) = func_returning_result() {
+LL | | return Err(err);
+LL | | }
+ | |_____^ help: replace it with: `func_returning_result()?;`
+
+error: this block may be rewritten with the `?` operator
+ --> $DIR/question_mark.rs:200:5
+ |
+LL | / if let Err(err) = func_returning_result() {
+LL | | return Err(err);
+LL | | }
+ | |_____^ help: replace it with: `func_returning_result()?;`
+
+error: aborting due to 15 previous errors
impl T1 for S {}
}
- mod mulitply_conflicit_trait {
+ mod multiply_conflicit_trait {
use crate::{T1, T2};
struct S;
let _ = v.iter().any(|fp| test_u32_2(*fp.field));
}
}
+
+mod issue9120 {
+ fn make_arg_no_deref_impl() -> impl Fn(&&u32) -> bool {
+ move |x: &&u32| **x == 78
+ }
+
+ fn make_arg_no_deref_dyn() -> Box<dyn Fn(&&u32) -> bool> {
+ Box::new(move |x: &&u32| **x == 78)
+ }
+
+ fn wrapper<T: Fn(&&u32) -> bool>(v: Vec<u32>, func: T) -> bool {
+ #[allow(clippy::redundant_closure)]
+ v.iter().any(|x: &u32| func(&x))
+ }
+
+ fn do_tests() {
+ let v = vec![3, 2, 1, 0];
+ let arg_no_deref_impl = make_arg_no_deref_impl();
+ let arg_no_deref_dyn = make_arg_no_deref_dyn();
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().any(|x: &u32| arg_no_deref_impl(&x));
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().any(|x: &u32| arg_no_deref_dyn(&x));
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().any(|x: &u32| (*arg_no_deref_dyn)(&x));
+ }
+}
let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
}
}
+
+mod issue9120 {
+ fn make_arg_no_deref_impl() -> impl Fn(&&u32) -> bool {
+ move |x: &&u32| **x == 78
+ }
+
+ fn make_arg_no_deref_dyn() -> Box<dyn Fn(&&u32) -> bool> {
+ Box::new(move |x: &&u32| **x == 78)
+ }
+
+ fn wrapper<T: Fn(&&u32) -> bool>(v: Vec<u32>, func: T) -> bool {
+ #[allow(clippy::redundant_closure)]
+ v.iter().find(|x: &&u32| func(x)).is_some()
+ }
+
+ fn do_tests() {
+ let v = vec![3, 2, 1, 0];
+ let arg_no_deref_impl = make_arg_no_deref_impl();
+ let arg_no_deref_dyn = make_arg_no_deref_dyn();
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some();
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some();
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some();
+ }
+}
LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))`
-error: aborting due to 43 previous errors
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:234:18
+ |
+LL | v.iter().find(|x: &&u32| func(x)).is_some()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| func(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:243:26
+ |
+LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref_impl(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:246:26
+ |
+LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref_dyn(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:249:26
+ |
+LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| (*arg_no_deref_dyn)(&x))`
+
+error: aborting due to 47 previous errors
LL | pub async fn foo2(_a: i32, _b: i64) {
| ^^
-error: aborting due to 22 previous errors
+error: `x` shadows a previous, unrelated binding
+ --> $DIR/shadow.rs:94:21
+ |
+LL | if let Some(x) = Some(1) { x } else { 1 }
+ | ^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:93:13
+ |
+LL | let x = 1;
+ | ^
+
+error: aborting due to 23 previous errors
};
}
+fn should_not_trigger_on_significant_iterator_drop() {
+ let lines = std::io::stdin().lines();
+ for line in lines {
+ println!("foo: {}", line.unwrap());
+ }
+}
+
fn main() {}
--- /dev/null
+#![warn(clippy::std_instead_of_core)]
+#![allow(unused_imports)]
+
+extern crate alloc;
+
+#[warn(clippy::std_instead_of_core)]
+fn std_instead_of_core() {
+ // Regular import
+ use std::hash::Hasher;
+ // Absolute path
+ use ::std::hash::Hash;
+
+ // Multiple imports
+ use std::fmt::{Debug, Result};
+
+ // Function calls
+ let ptr = std::ptr::null::<u32>();
+ let ptr_mut = ::std::ptr::null_mut::<usize>();
+
+ // Types
+ let cell = std::cell::Cell::new(8u32);
+ let cell_absolute = ::std::cell::Cell::new(8u32);
+}
+
+#[warn(clippy::std_instead_of_alloc)]
+fn std_instead_of_alloc() {
+ use std::vec::Vec;
+}
+
+#[warn(clippy::alloc_instead_of_core)]
+fn alloc_instead_of_core() {
+ use alloc::slice::from_ref;
+}
+
+fn main() {
+ std_instead_of_core();
+ std_instead_of_alloc();
+ alloc_instead_of_core();
+}
--- /dev/null
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:9:9
+ |
+LL | use std::hash::Hasher;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::std-instead-of-core` implied by `-D warnings`
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:11:9
+ |
+LL | use ::std::hash::Hash;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:14:20
+ |
+LL | use std::fmt::{Debug, Result};
+ | ^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:14:27
+ |
+LL | use std::fmt::{Debug, Result};
+ | ^^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:17:15
+ |
+LL | let ptr = std::ptr::null::<u32>();
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:18:19
+ |
+LL | let ptr_mut = ::std::ptr::null_mut::<usize>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:21:16
+ |
+LL | let cell = std::cell::Cell::new(8u32);
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+ --> $DIR/std_instead_of_core.rs:22:25
+ |
+LL | let cell_absolute = ::std::cell::Cell::new(8u32);
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider importing the item from `core`
+
+error: used import from `std` instead of `alloc`
+ --> $DIR/std_instead_of_core.rs:27:9
+ |
+LL | use std::vec::Vec;
+ | ^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings`
+ = help: consider importing the item from `alloc`
+
+error: used import from `alloc` instead of `core`
+ --> $DIR/std_instead_of_core.rs:32:9
+ |
+LL | use alloc::slice::from_ref;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings`
+ = help: consider importing the item from `core`
+
+error: aborting due to 10 previous errors
+
#![deny(clippy::trait_duplication_in_bounds)]
+#![allow(unused)]
use std::collections::BTreeMap;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
// This should not lint
fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+mod repeated_where_clauses_or_trait_bounds {
+ fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+ unimplemented!();
+ }
+
+ fn bad_bar<T, U>(arg0: T, arg1: U)
+ where
+ T: Clone + Clone + Clone + Copy,
+ U: Clone + Copy,
+ {
+ unimplemented!();
+ }
+
+ fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
+ unimplemented!();
+ }
+
+ fn good_foo<T, U>(arg0: T, arg1: U)
+ where
+ T: Clone + Copy,
+ U: Clone + Copy,
+ {
+ unimplemented!();
+ }
+
+ trait GoodSelfTraitBound: Clone + Copy {
+ fn f();
+ }
+
+ trait GoodSelfWhereClause {
+ fn f()
+ where
+ Self: Clone + Copy;
+ }
+
+ trait BadSelfTraitBound: Clone + Clone + Clone {
+ fn f();
+ }
+
+ trait BadSelfWhereClause {
+ fn f()
+ where
+ Self: Clone + Clone + Clone;
+ }
+
+ trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
+ fn f();
+ }
+
+ trait GoodWhereClause<T, U> {
+ fn f()
+ where
+ T: Clone + Copy,
+ U: Clone + Copy;
+ }
+
+ trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+ fn f();
+ }
+
+ trait BadWhereClause<T, U> {
+ fn f()
+ where
+ T: Clone + Clone + Clone + Copy,
+ U: Clone + Copy;
+ }
+
+ struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
+ t: T,
+ u: U,
+ }
+
+ impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
+ // this should not warn
+ fn f() {}
+ }
+
+ struct GoodStructWhereClause;
+
+ impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
+ where
+ T: Clone + Copy,
+ U: Clone + Copy,
+ {
+ // this should not warn
+ fn f() {}
+ }
+
+ fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
+
+ trait GenericTrait<T> {}
+
+ // This should not warn but currently does see #8757
+ fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
+ unimplemented!();
+ }
+
+ fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+ unimplemented!();
+ }
+
+ mod foo {
+ pub trait Clone {}
+ }
+
+ fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+ unimplemented!();
+ }
+}
+
fn main() {}
error: this trait bound is already specified in the where clause
- --> $DIR/trait_duplication_in_bounds.rs:6:15
+ --> $DIR/trait_duplication_in_bounds.rs:7:15
|
LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
| ^^^^^
= help: consider removing this trait bound
error: this trait bound is already specified in the where clause
- --> $DIR/trait_duplication_in_bounds.rs:6:23
+ --> $DIR/trait_duplication_in_bounds.rs:7:23
|
LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
| ^^^^^^^
= help: consider removing this trait bound
error: this trait bound is already specified in trait declaration
- --> $DIR/trait_duplication_in_bounds.rs:35:15
+ --> $DIR/trait_duplication_in_bounds.rs:36:15
|
LL | Self: Default;
| ^^^^^^^
= help: consider removing this trait bound
error: this trait bound is already specified in trait declaration
- --> $DIR/trait_duplication_in_bounds.rs:49:15
+ --> $DIR/trait_duplication_in_bounds.rs:50:15
|
LL | Self: Default + Clone;
| ^^^^^^^
= help: consider removing this trait bound
error: this trait bound is already specified in trait declaration
- --> $DIR/trait_duplication_in_bounds.rs:55:15
+ --> $DIR/trait_duplication_in_bounds.rs:56:15
|
LL | Self: Default + Clone;
| ^^^^^^^
= help: consider removing this trait bound
error: this trait bound is already specified in trait declaration
- --> $DIR/trait_duplication_in_bounds.rs:55:25
+ --> $DIR/trait_duplication_in_bounds.rs:56:25
|
LL | Self: Default + Clone;
| ^^^^^
= help: consider removing this trait bound
error: this trait bound is already specified in trait declaration
- --> $DIR/trait_duplication_in_bounds.rs:58:15
+ --> $DIR/trait_duplication_in_bounds.rs:59:15
|
LL | Self: Default;
| ^^^^^^^
= help: consider removing this trait bound
error: this trait bound is already specified in trait declaration
- --> $DIR/trait_duplication_in_bounds.rs:93:15
+ --> $DIR/trait_duplication_in_bounds.rs:94:15
|
LL | Self: Iterator<Item = Foo>,
| ^^^^^^^^^^^^^^^^^^^^
|
= help: consider removing this trait bound
-error: aborting due to 8 previous errors
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:103:19
+ |
+LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+ | ^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: these bounds contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:103:19
+ |
+LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:109:12
+ |
+LL | T: Clone + Clone + Clone + Copy,
+ | ^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: these where clauses contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:109:12
+ |
+LL | T: Clone + Clone + Clone + Copy,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+
+error: these bounds contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:137:30
+ |
+LL | trait BadSelfTraitBound: Clone + Clone + Clone {
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
+
+error: these where clauses contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:144:19
+ |
+LL | Self: Clone + Clone + Clone;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:158:28
+ |
+LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+ | ^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: these bounds contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:158:28
+ |
+LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+
+error: these where clauses contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:165:16
+ |
+LL | T: Clone + Clone + Clone + Copy,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:195:24
+ |
+LL | fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:199:23
+ |
+LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: these bounds contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:199:23
+ |
+LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u32> + GenericTrait<u64>`
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:207:26
+ |
+LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: these bounds contain repeated elements
+ --> $DIR/trait_duplication_in_bounds.rs:207:26
+ |
+LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + foo::Clone`
+
+error: aborting due to 22 previous errors
// run-rustfix
#![warn(clippy::transmutes_expressible_as_ptr_casts)]
-// These two warnings currrently cover the cases transmutes_expressible_as_ptr_casts
+// These two warnings currently cover the cases transmutes_expressible_as_ptr_casts
// would otherwise be responsible for
#![warn(clippy::useless_transmute)]
#![warn(clippy::transmute_ptr_to_ptr)]
// run-rustfix
#![warn(clippy::transmutes_expressible_as_ptr_casts)]
-// These two warnings currrently cover the cases transmutes_expressible_as_ptr_casts
+// These two warnings currently cover the cases transmutes_expressible_as_ptr_casts
// would otherwise be responsible for
#![warn(clippy::useless_transmute)]
#![warn(clippy::transmute_ptr_to_ptr)]
u: U,
}
+// Check for the `?` in `?Sized`
+pub fn f<T: ?Sized>()
+where
+ T: Clone,
+{
+}
+pub fn g<T: Clone>()
+where
+ T: ?Sized,
+{
+}
+
// This should not lint
fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
|
= help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
-error: aborting due to 2 previous errors
+error: this type has already been used as a bound predicate
+ --> $DIR/type_repetition_in_bounds.rs:85:5
+ |
+LL | T: Clone,
+ | ^^^^^^^^
+ |
+ = help: consider combining the bounds: `T: ?Sized + Clone`
+
+error: this type has already been used as a bound predicate
+ --> $DIR/type_repetition_in_bounds.rs:90:5
+ |
+LL | T: ?Sized,
+ | ^^^^^^^^^
+ |
+ = help: consider combining the bounds: `T: Clone + ?Sized`
+
+error: aborting due to 4 previous errors
proc_macro_unsafe::unsafe_block!(token);
}
+fn in_closure(x: *const u32) {
+ // Safety: reason
+ let _ = || unsafe { *x };
+}
+
// Invalid comments
#[rustfmt::skip]
#[rustfmt::skip]
mod sub_mod2 {
- //
+ //
// SAFETY: ok
- //
+ //
unsafe impl B for (u32) {}
unsafe trait B {}
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:257:19
+ --> $DIR/undocumented_unsafe_blocks.rs:262:19
|
LL | /* Safety: */ unsafe {}
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:261:5
+ --> $DIR/undocumented_unsafe_blocks.rs:266:5
|
LL | unsafe {}
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:265:14
+ --> $DIR/undocumented_unsafe_blocks.rs:270:14
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
| ^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:265:29
+ --> $DIR/undocumented_unsafe_blocks.rs:270:29
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
| ^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:265:48
+ --> $DIR/undocumented_unsafe_blocks.rs:270:48
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
| ^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:269:18
+ --> $DIR/undocumented_unsafe_blocks.rs:274:18
|
LL | let _ = (42, unsafe {}, "test", unsafe {});
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:269:37
+ --> $DIR/undocumented_unsafe_blocks.rs:274:37
|
LL | let _ = (42, unsafe {}, "test", unsafe {});
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:273:14
+ --> $DIR/undocumented_unsafe_blocks.rs:278:14
|
LL | let _ = *unsafe { &42 };
| ^^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:278:19
+ --> $DIR/undocumented_unsafe_blocks.rs:283:19
|
LL | let _ = match unsafe {} {
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:284:14
+ --> $DIR/undocumented_unsafe_blocks.rs:289:14
|
LL | let _ = &unsafe {};
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:288:14
+ --> $DIR/undocumented_unsafe_blocks.rs:293:14
|
LL | let _ = [unsafe {}; 5];
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:292:13
+ --> $DIR/undocumented_unsafe_blocks.rs:297:13
|
LL | let _ = unsafe {};
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:302:8
+ --> $DIR/undocumented_unsafe_blocks.rs:307:8
|
LL | t!(unsafe {});
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:308:13
+ --> $DIR/undocumented_unsafe_blocks.rs:313:13
|
LL | unsafe {}
| ^^^^^^^^^
= note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:316:5
+ --> $DIR/undocumented_unsafe_blocks.rs:321:5
|
LL | unsafe {} // SAFETY:
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:320:5
+ --> $DIR/undocumented_unsafe_blocks.rs:325:5
|
LL | unsafe {
| ^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:330:5
+ --> $DIR/undocumented_unsafe_blocks.rs:335:5
|
LL | unsafe {};
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:334:20
+ --> $DIR/undocumented_unsafe_blocks.rs:339:20
|
LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:341:5
+ --> $DIR/undocumented_unsafe_blocks.rs:346:5
|
LL | unsafe impl A for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:348:9
+ --> $DIR/undocumented_unsafe_blocks.rs:353:9
|
LL | unsafe impl B for (u32) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:369:13
+ --> $DIR/undocumented_unsafe_blocks.rs:374:13
|
LL | unsafe impl T for $t {}
| ^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:394:13
+ --> $DIR/undocumented_unsafe_blocks.rs:399:13
|
LL | unsafe impl T for $t {}
| ^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:402:5
+ --> $DIR/undocumented_unsafe_blocks.rs:407:5
|
LL | unsafe impl T for (i32) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:394:13
+ --> $DIR/undocumented_unsafe_blocks.rs:399:13
|
LL | unsafe impl T for $t {}
| ^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:408:5
+ --> $DIR/undocumented_unsafe_blocks.rs:413:5
|
LL | unsafe impl T for (bool) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:454:5
+ --> $DIR/undocumented_unsafe_blocks.rs:459:5
|
LL | unsafe impl NoComment for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:458:19
+ --> $DIR/undocumented_unsafe_blocks.rs:463:19
|
LL | /* SAFETY: */ unsafe impl InlineComment for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:462:5
+ --> $DIR/undocumented_unsafe_blocks.rs:467:5
|
LL | unsafe impl TrailingComment for () {} // SAFETY:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:467:5
+ --> $DIR/undocumented_unsafe_blocks.rs:472:5
|
LL | unsafe impl Interference for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:474:5
+ --> $DIR/undocumented_unsafe_blocks.rs:479:5
|
LL | unsafe impl ImplInFn for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:483:1
+ --> $DIR/undocumented_unsafe_blocks.rs:488:1
|
LL | unsafe impl CrateRoot for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
let ext_opt = Some(42);
let nested_opt = Some(Some(42));
let nested_tuple_opt = Some(Some((42, 43)));
+ let cond = true;
// Should lint - Option
let _ = opt.unwrap_or(2);
let _ = opt.get_or_insert(2);
let _ = opt.ok_or(2);
let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
+ let _ = cond.then_some(astronomers_pi);
// Cases when unwrap is not called on a simple variable
let _ = Some(10).unwrap_or(2);
let ext_opt = Some(42);
let nested_opt = Some(Some(42));
let nested_tuple_opt = Some(Some((42, 43)));
+ let cond = true;
// Should lint - Option
let _ = opt.unwrap_or_else(|| 2);
let _ = opt.get_or_insert_with(|| 2);
let _ = opt.ok_or_else(|| 2);
let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
+ let _ = cond.then(|| astronomers_pi);
// Cases when unwrap is not called on a simple variable
let _ = Some(10).unwrap_or_else(|| 2);
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:35:13
+ --> $DIR/unnecessary_lazy_eval.rs:36:13
|
LL | let _ = opt.unwrap_or_else(|| 2);
| ^^^^--------------------
= note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:36:13
+ --> $DIR/unnecessary_lazy_eval.rs:37:13
|
LL | let _ = opt.unwrap_or_else(|| astronomers_pi);
| ^^^^---------------------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:37:13
+ --> $DIR/unnecessary_lazy_eval.rs:38:13
|
LL | let _ = opt.unwrap_or_else(|| ext_str.some_field);
| ^^^^-------------------------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:39:13
+ --> $DIR/unnecessary_lazy_eval.rs:40:13
|
LL | let _ = opt.and_then(|_| ext_opt);
| ^^^^---------------------
| help: use `and(..)` instead: `and(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:40:13
+ --> $DIR/unnecessary_lazy_eval.rs:41:13
|
LL | let _ = opt.or_else(|| ext_opt);
| ^^^^-------------------
| help: use `or(..)` instead: `or(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:41:13
+ --> $DIR/unnecessary_lazy_eval.rs:42:13
|
LL | let _ = opt.or_else(|| None);
| ^^^^----------------
| help: use `or(..)` instead: `or(None)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:42:13
+ --> $DIR/unnecessary_lazy_eval.rs:43:13
|
LL | let _ = opt.get_or_insert_with(|| 2);
| ^^^^------------------------
| help: use `get_or_insert(..)` instead: `get_or_insert(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:43:13
+ --> $DIR/unnecessary_lazy_eval.rs:44:13
|
LL | let _ = opt.ok_or_else(|| 2);
| ^^^^----------------
| help: use `ok_or(..)` instead: `ok_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:44:13
+ --> $DIR/unnecessary_lazy_eval.rs:45:13
|
LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
| ^^^^^^^^^^^^^^^^^-------------------------------
| |
| help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))`
+error: unnecessary closure used with `bool::then`
+ --> $DIR/unnecessary_lazy_eval.rs:46:13
+ |
+LL | let _ = cond.then(|| astronomers_pi);
+ | ^^^^^-----------------------
+ | |
+ | help: use `then_some(..)` instead: `then_some(astronomers_pi)`
+
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:47:13
+ --> $DIR/unnecessary_lazy_eval.rs:49:13
|
LL | let _ = Some(10).unwrap_or_else(|| 2);
| ^^^^^^^^^--------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:48:13
+ --> $DIR/unnecessary_lazy_eval.rs:50:13
|
LL | let _ = Some(10).and_then(|_| ext_opt);
| ^^^^^^^^^---------------------
| help: use `and(..)` instead: `and(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:49:28
+ --> $DIR/unnecessary_lazy_eval.rs:51:28
|
LL | let _: Option<usize> = None.or_else(|| ext_opt);
| ^^^^^-------------------
| help: use `or(..)` instead: `or(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:50:13
+ --> $DIR/unnecessary_lazy_eval.rs:52:13
|
LL | let _ = None.get_or_insert_with(|| 2);
| ^^^^^------------------------
| help: use `get_or_insert(..)` instead: `get_or_insert(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:51:35
+ --> $DIR/unnecessary_lazy_eval.rs:53:35
|
LL | let _: Result<usize, usize> = None.ok_or_else(|| 2);
| ^^^^^----------------
| help: use `ok_or(..)` instead: `ok_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:52:28
+ --> $DIR/unnecessary_lazy_eval.rs:54:28
|
LL | let _: Option<usize> = None.or_else(|| None);
| ^^^^^----------------
| help: use `or(..)` instead: `or(None)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:55:13
+ --> $DIR/unnecessary_lazy_eval.rs:57:13
|
LL | let _ = deep.0.unwrap_or_else(|| 2);
| ^^^^^^^--------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:56:13
+ --> $DIR/unnecessary_lazy_eval.rs:58:13
|
LL | let _ = deep.0.and_then(|_| ext_opt);
| ^^^^^^^---------------------
| help: use `and(..)` instead: `and(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:57:13
+ --> $DIR/unnecessary_lazy_eval.rs:59:13
|
LL | let _ = deep.0.or_else(|| None);
| ^^^^^^^----------------
| help: use `or(..)` instead: `or(None)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:58:13
+ --> $DIR/unnecessary_lazy_eval.rs:60:13
|
LL | let _ = deep.0.get_or_insert_with(|| 2);
| ^^^^^^^------------------------
| help: use `get_or_insert(..)` instead: `get_or_insert(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:59:13
+ --> $DIR/unnecessary_lazy_eval.rs:61:13
|
LL | let _ = deep.0.ok_or_else(|| 2);
| ^^^^^^^----------------
| help: use `ok_or(..)` instead: `ok_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:79:28
+ --> $DIR/unnecessary_lazy_eval.rs:81:28
|
LL | let _: Option<usize> = None.or_else(|| Some(3));
| ^^^^^-------------------
| help: use `or(..)` instead: `or(Some(3))`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:80:13
+ --> $DIR/unnecessary_lazy_eval.rs:82:13
|
LL | let _ = deep.0.or_else(|| Some(3));
| ^^^^^^^-------------------
| help: use `or(..)` instead: `or(Some(3))`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:81:13
+ --> $DIR/unnecessary_lazy_eval.rs:83:13
|
LL | let _ = opt.or_else(|| Some(3));
| ^^^^-------------------
| help: use `or(..)` instead: `or(Some(3))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:87:13
+ --> $DIR/unnecessary_lazy_eval.rs:89:13
|
LL | let _ = res2.unwrap_or_else(|_| 2);
| ^^^^^---------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:88:13
+ --> $DIR/unnecessary_lazy_eval.rs:90:13
|
LL | let _ = res2.unwrap_or_else(|_| astronomers_pi);
| ^^^^^----------------------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:89:13
+ --> $DIR/unnecessary_lazy_eval.rs:91:13
|
LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field);
| ^^^^^--------------------------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:111:35
+ --> $DIR/unnecessary_lazy_eval.rs:113:35
|
LL | let _: Result<usize, usize> = res.and_then(|_| Err(2));
| ^^^^--------------------
| help: use `and(..)` instead: `and(Err(2))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:112:35
+ --> $DIR/unnecessary_lazy_eval.rs:114:35
|
LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
| ^^^^---------------------------------
| help: use `and(..)` instead: `and(Err(astronomers_pi))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:113:35
+ --> $DIR/unnecessary_lazy_eval.rs:115:35
|
LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
| ^^^^-------------------------------------
| help: use `and(..)` instead: `and(Err(ext_str.some_field))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:115:35
+ --> $DIR/unnecessary_lazy_eval.rs:117:35
|
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2));
| ^^^^------------------
| help: use `or(..)` instead: `or(Ok(2))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:116:35
+ --> $DIR/unnecessary_lazy_eval.rs:118:35
|
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
| ^^^^-------------------------------
| help: use `or(..)` instead: `or(Ok(astronomers_pi))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:117:35
+ --> $DIR/unnecessary_lazy_eval.rs:119:35
|
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
| ^^^^-----------------------------------
| help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:118:35
+ --> $DIR/unnecessary_lazy_eval.rs:120:35
|
LL | let _: Result<usize, usize> = res.
| ___________________________________^
| |
| help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
-error: aborting due to 33 previous errors
+error: aborting due to 34 previous errors
unused_mut,
dead_code,
clippy::equatable_if_let,
- clippy::manual_find
+ clippy::manual_find,
+ clippy::redundant_closure_call
)]
fn base() {
fn f(&mut self) -> Option<u32> {
// Used as a field.
for i in self.0.by_ref() {
- if !(3..=7).contains(&i) {
+ if !(3..8).contains(&i) {
return Some(i);
}
}
}
}
+fn fn_once_closure() {
+ let mut it = 0..10;
+ (|| {
+ for x in it {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ })();
+
+ fn f(_: impl FnOnce()) {}
+ let mut it = 0..10;
+ f(|| {
+ for x in it {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ });
+
+ fn f2(_: impl FnMut()) {}
+ let mut it = 0..10;
+ f2(|| {
+ for x in it.by_ref() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ });
+
+ fn f3(_: fn()) {}
+ f3(|| {
+ let mut it = 0..10;
+ for x in it {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ })
+}
+
fn main() {
let mut it = 0..20;
for _ in it {
unused_mut,
dead_code,
clippy::equatable_if_let,
- clippy::manual_find
+ clippy::manual_find,
+ clippy::redundant_closure_call
)]
fn base() {
fn f(&mut self) -> Option<u32> {
// Used as a field.
while let Some(i) = self.0.next() {
- if i < 3 || i > 7 {
+ if !(3..8).contains(&i) {
return Some(i);
}
}
}
}
+fn fn_once_closure() {
+ let mut it = 0..10;
+ (|| {
+ while let Some(x) = it.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ })();
+
+ fn f(_: impl FnOnce()) {}
+ let mut it = 0..10;
+ f(|| {
+ while let Some(x) = it.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ });
+
+ fn f2(_: impl FnMut()) {}
+ let mut it = 0..10;
+ f2(|| {
+ while let Some(x) = it.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ });
+
+ fn f3(_: fn()) {}
+ f3(|| {
+ let mut it = 0..10;
+ while let Some(x) = it.next() {
+ if x % 2 == 0 {
+ break;
+ }
+ }
+ })
+}
+
fn main() {
let mut it = 0..20;
while let Some(..) = it.next() {
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:15:5
+ --> $DIR/while_let_on_iterator.rs:16:5
|
LL | while let Option::Some(x) = iter.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
= note: `-D clippy::while-let-on-iterator` implied by `-D warnings`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:20:5
+ --> $DIR/while_let_on_iterator.rs:21:5
|
LL | while let Some(x) = iter.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:25:5
+ --> $DIR/while_let_on_iterator.rs:26:5
|
LL | while let Some(_) = iter.next() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:101:9
+ --> $DIR/while_let_on_iterator.rs:102:9
|
LL | while let Some([..]) = it.next() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:108:9
+ --> $DIR/while_let_on_iterator.rs:109:9
|
LL | while let Some([_x]) = it.next() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:121:9
+ --> $DIR/while_let_on_iterator.rs:122:9
|
LL | while let Some(x @ [_]) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:141:9
+ --> $DIR/while_let_on_iterator.rs:142:9
|
LL | while let Some(_) = y.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:198:9
+ --> $DIR/while_let_on_iterator.rs:199:9
|
LL | while let Some(m) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:209:5
+ --> $DIR/while_let_on_iterator.rs:210:5
|
LL | while let Some(n) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:211:9
+ --> $DIR/while_let_on_iterator.rs:212:9
|
LL | while let Some(m) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:220:9
+ --> $DIR/while_let_on_iterator.rs:221:9
|
LL | while let Some(m) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:229:9
+ --> $DIR/while_let_on_iterator.rs:230:9
|
LL | while let Some(m) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:246:9
+ --> $DIR/while_let_on_iterator.rs:247:9
|
LL | while let Some(m) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:261:13
+ --> $DIR/while_let_on_iterator.rs:262:13
|
LL | while let Some(i) = self.0.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()`
-error: manual `!RangeInclusive::contains` implementation
- --> $DIR/while_let_on_iterator.rs:262:20
- |
-LL | if i < 3 || i > 7 {
- | ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)`
- |
- = note: `-D clippy::manual-range-contains` implied by `-D warnings`
-
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:293:13
+ --> $DIR/while_let_on_iterator.rs:294:13
|
LL | while let Some(i) = self.0.0.0.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:322:5
+ --> $DIR/while_let_on_iterator.rs:323:5
|
LL | while let Some(n) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:334:9
+ --> $DIR/while_let_on_iterator.rs:335:9
|
LL | while let Some(x) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:348:5
+ --> $DIR/while_let_on_iterator.rs:349:5
|
LL | while let Some(x) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:359:5
+ --> $DIR/while_let_on_iterator.rs:360:5
|
LL | while let Some(x) = it.0.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:394:5
+ --> $DIR/while_let_on_iterator.rs:395:5
|
LL | while let Some(x) = s.x.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:401:5
+ --> $DIR/while_let_on_iterator.rs:402:5
|
LL | while let Some(x) = x[0].next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()`
error: this loop could be written as a `for` loop
- --> $DIR/while_let_on_iterator.rs:408:5
+ --> $DIR/while_let_on_iterator.rs:410:9
+ |
+LL | while let Some(x) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:420:9
+ |
+LL | while let Some(x) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:430:9
+ |
+LL | while let Some(x) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:440:9
+ |
+LL | while let Some(x) = it.next() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
+
+error: this loop could be written as a `for` loop
+ --> $DIR/while_let_on_iterator.rs:450:5
|
LL | while let Some(..) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
-error: aborting due to 23 previous errors
+error: aborting due to 26 previous errors
}
}
- mod should_be_replaced_futher_inside {
+ mod should_be_replaced_further_inside {
fn insidefoo() {}
mod inner {
use super::insidefoo;
}
}
- mod should_be_replaced_futher_inside {
+ mod should_be_replaced_further_inside {
fn insidefoo() {}
mod inner {
use super::*;
"problemMatcher": [],
"group": {
"kind": "build",
- "isDefault": true,
- },
+ "isDefault": true
+ }
},
{
"label": "cargo dev fmt",
"type": "shell",
"command": "cargo dev fmt",
"problemMatcher": [],
- "group": "none",
+ "group": "none"
},
{
"label": "cargo uitest",
"command": "cargo uitest",
"options": {
"env": {
- "RUST_BACKTRACE": "1",
// This task will usually execute all UI tests inside `tests/ui` you can
// optionally uncomment the line below and only run a specific test.
//
- // See: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md#testing
+ // See: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md#testing
//
// "TESTNAME": "<TODO>",
- },
+ "RUST_BACKTRACE": "1"
+ }
},
"problemMatcher": [],
"group": {
"kind": "test",
- "isDefault": true,
+ "isDefault": true
}
},
{
"type": "shell",
"command": "cargo test",
"problemMatcher": [],
- "group": "test",
+ "group": "test"
},
{
"label": "cargo dev bless",
"type": "shell",
"command": "cargo dev bless",
"problemMatcher": [],
- "group": "none",
- },
- ],
+ "group": "none"
+ }
+ ]
}
<title>Clippy Lints</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css"/>
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/github.min.css"/>
+ <link id="githubLightHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github.min.css" disabled="true" />
+ <link id="githubDarkHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github-dark.min.css" disabled="true" />
<!-- The files are not copied over into the Clippy project since they use the MPL-2.0 License -->
<link rel="stylesheet" href="https://rust-lang.github.io/mdBook/css/variables.css"/>
</a>
<script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/12.3.2/markdown-it.min.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/languages/rust.min.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/languages/rust.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script>
<script src="script.js"></script>
</body>
if (lang && hljs.getLanguage(lang)) {
try {
return '<pre class="hljs"><code>' +
- hljs.highlight(lang, str, true).value +
+ hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
'</code></pre>';
} catch (__) {}
}
document.getElementsByTagName("body")[0].className = theme;
+ document.getElementById("githubLightHighlight").disabled = enableNight || !enableHighlight;
+ document.getElementById("githubDarkHighlight").disabled = !enableNight && !enableAyu;
+
document.getElementById("styleHighlight").disabled = !enableHighlight;
document.getElementById("styleNight").disabled = !enableNight;
document.getElementById("styleAyu").disabled = !enableAyu;