Merge commit '3c06e0b1ce003912f8fe0536d3a7fe22558e38cf' into clippyup

This commit is contained in:
Philipp Krones 2023-03-10 10:53:50 +01:00
parent eceedd9c8b
commit cf8a67d9ad
114 changed files with 2508 additions and 775 deletions

View file

@ -6,11 +6,156 @@ document.
## Unreleased / Beta / In Rust Nightly ## Unreleased / Beta / In Rust Nightly
[d822110d...master](https://github.com/rust-lang/rust-clippy/compare/d822110d...master) [7f27e2e7...master](https://github.com/rust-lang/rust-clippy/compare/7f27e2e7...master)
## Rust 1.68
Current stable, released 2023-03-09
[d822110d...7f27e2e7](https://github.com/rust-lang/rust-clippy/compare/d822110d...7f27e2e7)
### New Lints
* [`permissions_set_readonly_false`]
[#10063](https://github.com/rust-lang/rust-clippy/pull/10063)
* [`almost_complete_range`]
[#10043](https://github.com/rust-lang/rust-clippy/pull/10043)
* [`size_of_ref`]
[#10098](https://github.com/rust-lang/rust-clippy/pull/10098)
* [`semicolon_outside_block`]
[#9826](https://github.com/rust-lang/rust-clippy/pull/9826)
* [`semicolon_inside_block`]
[#9826](https://github.com/rust-lang/rust-clippy/pull/9826)
* [`transmute_null_to_fn`]
[#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
* [`fn_null_check`]
[#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
### Moves and Deprecations
* Moved [`manual_clamp`] to `nursery` (Now allow-by-default)
[#10101](https://github.com/rust-lang/rust-clippy/pull/10101)
* Moved [`mutex_atomic`] to `restriction`
[#10115](https://github.com/rust-lang/rust-clippy/pull/10115)
* Renamed `derive_hash_xor_eq` to [`derived_hash_with_manual_eq`]
[#10184](https://github.com/rust-lang/rust-clippy/pull/10184)
### Enhancements
* [`collapsible_str_replace`]: Now takes MSRV into consideration. The minimal version is 1.58
[#10047](https://github.com/rust-lang/rust-clippy/pull/10047)
* [`unused_self`]: No longer lints, if the method body contains a `todo!()` call
[#10166](https://github.com/rust-lang/rust-clippy/pull/10166)
* [`derivable_impls`]: Now suggests deriving `Default` for enums with default unit variants
[#10161](https://github.com/rust-lang/rust-clippy/pull/10161)
* [`arithmetic_side_effects`]: Added two new config values
`arithmetic-side-effects-allowed-binary` and `arithmetic-side-effects-allowed-unary`
to allow operation on user types
[#9840](https://github.com/rust-lang/rust-clippy/pull/9840)
* [`large_const_arrays`], [`large_stack_arrays`]: avoid integer overflow when calculating
total array size
[#10103](https://github.com/rust-lang/rust-clippy/pull/10103)
* [`indexing_slicing`]: add new config `suppress-restriction-lint-in-const` to enable
restriction lints, even if the suggestion might not be applicable
[#9920](https://github.com/rust-lang/rust-clippy/pull/9920)
* [`needless_borrow`], [`redundant_clone`]: Now track references better and detect more cases
[#9701](https://github.com/rust-lang/rust-clippy/pull/9701)
* [`derived_hash_with_manual_eq`]: Now allows `#[derive(PartialEq)]` with custom `Hash`
implementations
[#10184](https://github.com/rust-lang/rust-clippy/pull/10184)
* [`manual_is_ascii_check`]: Now detects ranges with `.contains()` calls
[#10053](https://github.com/rust-lang/rust-clippy/pull/10053)
* [`transmuting_null`]: Now detects `const` pointers to all types
[#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
* [`needless_return`]: Now detects more cases for returns of owned values
[#10110](https://github.com/rust-lang/rust-clippy/pull/10110)
### False Positive Fixes
* [`field_reassign_with_default`]: No longer lints cases, where values are initializes from
closures capturing struct values
[#10143](https://github.com/rust-lang/rust-clippy/pull/10143)
* [`seek_to_start_instead_of_rewind`]: No longer lints, if the return of `seek` is used.
[#10096](https://github.com/rust-lang/rust-clippy/pull/10096)
* [`manual_filter`]: Now ignores if expressions where the else branch has side effects or
doesn't return `None`
[#10091](https://github.com/rust-lang/rust-clippy/pull/10091)
* [`implicit_clone`]: No longer lints if the type doesn't implement clone
[#10022](https://github.com/rust-lang/rust-clippy/pull/10022)
* [`match_wildcard_for_single_variants`]: No longer lints on wildcards with a guard
[#10056](https://github.com/rust-lang/rust-clippy/pull/10056)
* [`drop_ref`]: No longer lints idiomatic expression in `match` arms
[#10142](https://github.com/rust-lang/rust-clippy/pull/10142)
* [`arithmetic_side_effects`]: No longer lints on corner cases with negative number literals
[#9867](https://github.com/rust-lang/rust-clippy/pull/9867)
* [`string_lit_as_bytes`]: No longer lints in scrutinies of `match` statements
[#10012](https://github.com/rust-lang/rust-clippy/pull/10012)
* [`manual_assert`]: No longer lints in `else if` statements
[#10013](https://github.com/rust-lang/rust-clippy/pull/10013)
* [`needless_return`]: don't lint when using `do yeet`
[#10109](https://github.com/rust-lang/rust-clippy/pull/10109)
* All lints: No longer lint in enum discriminant values when the suggestion won't work in a
const context
[#10008](https://github.com/rust-lang/rust-clippy/pull/10008)
* [`single_element_loop`]: No longer lints, if the loop contains a `break` or `continue`
[#10162](https://github.com/rust-lang/rust-clippy/pull/10162)
* [`uninlined_format_args`]: No longer suggests inlining arguments in `assert!` and
`debug_assert!` macros before 2021 edition
[#10055](https://github.com/rust-lang/rust-clippy/pull/10055)
* [`explicit_counter_loop`]: No longer ignores counter changes after `continue` expressions
[#10094](https://github.com/rust-lang/rust-clippy/pull/10094)
* [`from_over_into`]: No longer lints on opaque types
[#9982](https://github.com/rust-lang/rust-clippy/pull/9982)
* [`expl_impl_clone_on_copy`]: No longer lints on `#[repr(packed)]` structs with generic
parameters
[#10189](https://github.com/rust-lang/rust-clippy/pull/10189)
### Suggestion Fixes/Improvements
* [`zero_ptr`]: Now suggests `core::` paths for `no_std` crates
[#10023](https://github.com/rust-lang/rust-clippy/pull/10023)
* [`useless_conversion`]: Now suggests removing calls to `into_iter()` on an expression
implementing `Iterator`
[#10020](https://github.com/rust-lang/rust-clippy/pull/10020)
* [`box_default`]: The suggestion now uses short paths
[#10153](https://github.com/rust-lang/rust-clippy/pull/10153)
* [`default_trait_access`], [`clone_on_copy`]: The suggestion now uses short paths
[#10160](https://github.com/rust-lang/rust-clippy/pull/10160)
* [`comparison_to_empty`]: The suggestion now removes unused deref operations
[#9962](https://github.com/rust-lang/rust-clippy/pull/9962)
* [`manual_let_else`]: Suggestions for or-patterns now include required brackets.
[#9966](https://github.com/rust-lang/rust-clippy/pull/9966)
* [`match_single_binding`]: suggestion no longer introduces unneeded semicolons
[#10060](https://github.com/rust-lang/rust-clippy/pull/10060)
* [`case_sensitive_file_extension_comparisons`]: Now displays a suggestion with `Path`
[#10107](https://github.com/rust-lang/rust-clippy/pull/10107)
* [`empty_structs_with_brackets`]: The suggestion is no longer machine applicable, to avoid
errors when accessing struct fields
[#10141](https://github.com/rust-lang/rust-clippy/pull/10141)
* [`identity_op`]: Removes borrows in the suggestion when needed
[#10004](https://github.com/rust-lang/rust-clippy/pull/10004)
* [`suboptimal_flops`]: The suggestion now includes parentheses when required
[#10113](https://github.com/rust-lang/rust-clippy/pull/10113)
* [`iter_kv_map`]: Now handles `mut` and reference annotations in the suggestion
[#10159](https://github.com/rust-lang/rust-clippy/pull/10159)
* [`redundant_static_lifetimes`]: The suggestion no longer removes `mut` from references
[#10006](https://github.com/rust-lang/rust-clippy/pull/10006)
### ICE Fixes
* [`new_ret_no_self`]: Now avoids a stack overflow for `impl Trait` types
[#10086](https://github.com/rust-lang/rust-clippy/pull/10086)
* [`unnecessary_to_owned`]: Now handles compiler generated notes better
[#10027](https://github.com/rust-lang/rust-clippy/pull/10027)
### Others
* `SYSROOT` and `--sysroot` can now be set at the same time
[#10149](https://github.com/rust-lang/rust-clippy/pull/10149)
## Rust 1.67 ## Rust 1.67
Current stable, released 2023-01-26 Released 2023-01-26
[4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d) [4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d)
@ -4307,6 +4452,7 @@ Released 2018-09-13
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace [`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
[`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
@ -4497,6 +4643,7 @@ Released 2018-09-13
[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
[`let_underscore_untyped`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped [`let_underscore_untyped`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped
[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
[`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
@ -4560,6 +4707,7 @@ Released 2018-09-13
[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order [`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
[`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters [`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters
[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
[`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message
[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
@ -4689,6 +4837,7 @@ Released 2018-09-13
[`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec [`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec
[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
[`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block
[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy" name = "clippy"
version = "0.1.69" version = "0.1.70"
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md" readme = "README.md"

View file

@ -68,13 +68,13 @@ The second part of the motivation is clippy's dependence on unstable
compiler-internal data structures. Clippy lints are currently written against compiler-internal data structures. Clippy lints are currently written against
the compiler's AST / HIR which means that even small changes in these data the compiler's AST / HIR which means that even small changes in these data
structures might break a lot of lints. The second goal of this RFC is to **make structures might break a lot of lints. The second goal of this RFC is to **make
lints independant of the compiler's AST / HIR data structures**. lints independent of the compiler's AST / HIR data structures**.
# Approach # Approach
A lot of complexity in writing lints currently seems to come from having to A lot of complexity in writing lints currently seems to come from having to
manually implement the matching logic (see code samples above). It's an manually implement the matching logic (see code samples above). It's an
imparative style that describes *how* to match a syntax tree node instead of imperative style that describes *how* to match a syntax tree node instead of
specifying *what* should be matched against declaratively. In other areas, it's specifying *what* should be matched against declaratively. In other areas, it's
common to use declarative patterns to describe desired information and let the common to use declarative patterns to describe desired information and let the
implementation do the actual matching. A well-known example of this approach are implementation do the actual matching. A well-known example of this approach are
@ -270,7 +270,7 @@ pattern!{
// matches if expressions that **may or may not** have an else block // matches if expressions that **may or may not** have an else block
// Attn: `If(_, _, _)` matches only ifs that **have** an else block // Attn: `If(_, _, _)` matches only ifs that **have** an else block
// //
// | if with else block | if witout else block // | if with else block | if without else block
// If(_, _, _) | match | no match // If(_, _, _) | match | no match
// If(_, _, _?) | match | match // If(_, _, _?) | match | match
// If(_, _, ()) | no match | match // If(_, _, ()) | no match | match
@ -568,7 +568,7 @@ another example, `Array( Lit(_)* )` is a valid pattern because the parameter of
## The IsMatch Trait ## The IsMatch Trait
The pattern syntax and the *PatternTree* are independant of specific syntax tree The pattern syntax and the *PatternTree* are independent of specific syntax tree
implementations (rust ast / hir, syn, ...). When looking at the different implementations (rust ast / hir, syn, ...). When looking at the different
pattern examples in the previous sections, it can be seen that the patterns pattern examples in the previous sections, it can be seen that the patterns
don't contain any information specific to a certain syntax tree implementation. don't contain any information specific to a certain syntax tree implementation.
@ -717,7 +717,7 @@ if false {
#### Problems #### Problems
Extending Rust syntax (which is quite complex by itself) with additional syntax Extending Rust syntax (which is quite complex by itself) with additional syntax
needed for specifying patterns (alternations, sequences, repetisions, named needed for specifying patterns (alternations, sequences, repetitions, named
submatches, ...) might become difficult to read and really hard to parse submatches, ...) might become difficult to read and really hard to parse
properly. properly.
@ -858,7 +858,7 @@ would be evaluated as soon as the `Block(_)#then` was matched.
Another idea in this area would be to introduce a syntax for backreferences. Another idea in this area would be to introduce a syntax for backreferences.
They could be used to require that multiple parts of a pattern should match the They could be used to require that multiple parts of a pattern should match the
same value. For example, the `assign_op_pattern` lint that searches for `a = a same value. For example, the `assign_op_pattern` lint that searches for `a = a
op b` and recommends changing it to `a op= b` requires that both occurrances of op b` and recommends changing it to `a op= b` requires that both occurrences of
`a` are the same. Using `=#...` as syntax for backreferences, the lint could be `a` are the same. Using `=#...` as syntax for backreferences, the lint could be
implemented like this: implemented like this:
@ -882,7 +882,7 @@ least two return statements" could be a practical addition.
For patterns like "a literal that is not a boolean literal" one currently needs For patterns like "a literal that is not a boolean literal" one currently needs
to list all alternatives except the boolean case. Introducing a negation to list all alternatives except the boolean case. Introducing a negation
operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern
would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three would be equivalent to `Lit( Char(_) | Int(_) )` (given that currently only three
literal types are implemented). literal types are implemented).
#### Functional composition #### Functional composition

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_lints" name = "clippy_lints"
version = "0.1.69" version = "0.1.70"
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md" readme = "README.md"

View file

@ -24,7 +24,7 @@ declare_clippy_lint! {
/// ```rust /// ```rust
/// let _ = 'a'..='z'; /// let _ = 'a'..='z';
/// ``` /// ```
#[clippy::version = "1.63.0"] #[clippy::version = "1.68.0"]
pub ALMOST_COMPLETE_RANGE, pub ALMOST_COMPLETE_RANGE,
suspicious, suspicious,
"almost complete range" "almost complete range"

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_context;
use clippy_utils::{match_def_path, paths}; use clippy_utils::{match_def_path, paths};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -34,6 +34,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
if let ExprKind::Path(ref qpath) = fun.kind; if let ExprKind::Path(ref qpath) = fun.kind;
if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
if let Some(rpk) = raw_parts_kind(cx, fun_def_id); if let Some(rpk) = raw_parts_kind(cx, fun_def_id);
let ctxt = expr.span.ctxt();
if cast_expr.span.ctxt() == ctxt;
then { then {
let func = match rpk { let func = match rpk {
RawPartsKind::Immutable => "from_raw_parts", RawPartsKind::Immutable => "from_raw_parts",
@ -41,8 +43,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
}; };
let span = expr.span; let span = expr.span;
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability); let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0;
let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability); let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0;
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
CAST_SLICE_FROM_RAW_PARTS, CAST_SLICE_FROM_RAW_PARTS,

View file

@ -0,0 +1,122 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::for_each_expr_with_closures;
use clippy_utils::{get_enclosing_block, get_parent_node, path_to_local_id};
use core::ops::ControlFlow;
use rustc_hir::{Block, ExprKind, HirId, Local, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
use rustc_span::Symbol;
declare_clippy_lint! {
/// ### What it does
/// Checks for collections that are never queried.
///
/// ### Why is this bad?
/// Putting effort into constructing a collection but then never querying it might indicate that
/// the author forgot to do whatever they intended to do with the collection. Example: Clone
/// a vector, sort it for iteration, but then mistakenly iterate the original vector
/// instead.
///
/// ### Example
/// ```rust
/// # let samples = vec![3, 1, 2];
/// let mut sorted_samples = samples.clone();
/// sorted_samples.sort();
/// for sample in &samples { // Oops, meant to use `sorted_samples`.
/// println!("{sample}");
/// }
/// ```
/// Use instead:
/// ```rust
/// # let samples = vec![3, 1, 2];
/// let mut sorted_samples = samples.clone();
/// sorted_samples.sort();
/// for sample in &sorted_samples {
/// println!("{sample}");
/// }
/// ```
#[clippy::version = "1.69.0"]
pub COLLECTION_IS_NEVER_READ,
nursery,
"a collection is never queried"
}
declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
static COLLECTIONS: [Symbol; 10] = [
sym::BTreeMap,
sym::BTreeSet,
sym::BinaryHeap,
sym::HashMap,
sym::HashSet,
sym::LinkedList,
sym::Option,
sym::String,
sym::Vec,
sym::VecDeque,
];
impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
// Look for local variables whose type is a container. Search surrounding bock for read access.
let ty = cx.typeck_results().pat_ty(local.pat);
if COLLECTIONS.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
&& let PatKind::Binding(_, local_id, _, _) = local.pat.kind
&& let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
&& has_no_read_access(cx, local_id, enclosing_block)
{
span_lint(cx, COLLECTION_IS_NEVER_READ, local.span, "collection is never read");
}
}
}
fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Block<'tcx>) -> bool {
let mut has_access = false;
let mut has_read_access = false;
// Inspect all expressions and sub-expressions in the block.
for_each_expr_with_closures(cx, block, |expr| {
// Ignore expressions that are not simply `id`.
if !path_to_local_id(expr, id) {
return ControlFlow::Continue(());
}
// `id` is being accessed. Investigate if it's a read access.
has_access = true;
// `id` appearing in the left-hand side of an assignment is not a read access:
//
// id = ...; // Not reading `id`.
if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id)
&& let ExprKind::Assign(lhs, ..) = parent.kind
&& path_to_local_id(lhs, id)
{
return ControlFlow::Continue(());
}
// Method call on `id` in a statement ignores any return value, so it's not a read access:
//
// id.foo(...); // Not reading `id`.
//
// Only assuming this for "official" methods defined on the type. For methods defined in extension
// traits (identified as local, based on the orphan rule), pessimistically assume that they might
// have side effects, so consider them a read.
if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id)
&& let ExprKind::MethodCall(_, receiver, _, _) = parent.kind
&& path_to_local_id(receiver, id)
&& let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id)
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
&& !method_def_id.is_local()
{
return ControlFlow::Continue(());
}
// Any other access to `id` is a read access. Stop searching.
has_read_access = true;
ControlFlow::Break(())
});
// Ignore collections that have no access at all. Other lints should catch them.
has_access && !has_read_access
}

View file

@ -92,6 +92,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
crate::collapsible_if::COLLAPSIBLE_IF_INFO, crate::collapsible_if::COLLAPSIBLE_IF_INFO,
crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO,
crate::comparison_chain::COMPARISON_CHAIN_INFO, crate::comparison_chain::COMPARISON_CHAIN_INFO,
crate::copies::BRANCHES_SHARING_CODE_INFO, crate::copies::BRANCHES_SHARING_CODE_INFO,
crate::copies::IFS_SAME_COND_INFO, crate::copies::IFS_SAME_COND_INFO,
@ -226,6 +227,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::let_underscore::LET_UNDERSCORE_LOCK_INFO, crate::let_underscore::LET_UNDERSCORE_LOCK_INFO,
crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO, crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO, crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO,
crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO,
crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
crate::lifetimes::NEEDLESS_LIFETIMES_INFO, crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO, crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,
@ -416,6 +418,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO, crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO,
crate::misc_early::ZERO_PREFIXED_LITERAL_INFO, crate::misc_early::ZERO_PREFIXED_LITERAL_INFO,
crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO, crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO,
crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO,
crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO, crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO,
crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO, crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO, crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
@ -517,6 +520,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::ranges::REVERSED_EMPTY_RANGES_INFO, crate::ranges::REVERSED_EMPTY_RANGES_INFO,
crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO, crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO,
crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO, crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO,
crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO,
crate::redundant_clone::REDUNDANT_CLONE_INFO, crate::redundant_clone::REDUNDANT_CLONE_INFO,
crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO, crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO,
crate::redundant_else::REDUNDANT_ELSE_INFO, crate::redundant_else::REDUNDANT_ELSE_INFO,

View file

@ -1,11 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::last_path_segment; use clippy_utils::last_path_segment;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_context;
use clippy_utils::{match_def_path, paths}; use clippy_utils::{match_def_path, paths};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind}; use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::SyntaxContext;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -38,9 +39,11 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
&& let QPath::Resolved(None, path) = ty_path && let QPath::Resolved(None, path) = ty_path
&& let def::Res::Def(_, def_id) = &path.res && let def::Res::Def(_, def_id) = &path.res
&& match_def_path(cx, *def_id, &paths::ITER_EMPTY) && match_def_path(cx, *def_id, &paths::ITER_EMPTY)
&& let ctxt = expr.span.ctxt()
&& ty.span.ctxt() == ctxt
{ {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let sugg = make_sugg(cx, ty_path, &mut applicability); let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability);
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
DEFAULT_INSTEAD_OF_ITER_EMPTY, DEFAULT_INSTEAD_OF_ITER_EMPTY,
@ -54,14 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
} }
} }
fn make_sugg(cx: &LateContext<'_>, ty_path: &rustc_hir::QPath<'_>, applicability: &mut Applicability) -> String { fn make_sugg(
cx: &LateContext<'_>,
ty_path: &rustc_hir::QPath<'_>,
ctxt: SyntaxContext,
applicability: &mut Applicability,
) -> String {
if let Some(last) = last_path_segment(ty_path).args if let Some(last) = last_path_segment(ty_path).args
&& let Some(iter_ty) = last.args.iter().find_map(|arg| match arg { && let Some(iter_ty) = last.args.iter().find_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty), GenericArg::Type(ty) => Some(ty),
_ => None, _ => None,
}) })
{ {
format!("std::iter::empty::<{}>()", snippet_with_applicability(cx, iter_ty.span, "..", applicability)) format!("std::iter::empty::<{}>()", snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0)
} else { } else {
"std::iter::empty()".to_owned() "std::iter::empty()".to_owned()
} }

View file

@ -1357,10 +1357,10 @@ fn replace_types<'tcx>(
&& let Some(term_ty) = projection_predicate.term.ty() && let Some(term_ty) = projection_predicate.term.ty()
&& let ty::Param(term_param_ty) = term_ty.kind() && let ty::Param(term_param_ty) = term_ty.kind()
{ {
let item_def_id = projection_predicate.projection_ty.def_id; let projection = cx.tcx.mk_ty_from_kind(ty::Alias(
let assoc_item = cx.tcx.associated_item(item_def_id); ty::Projection,
let projection = cx.tcx projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty),
.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, [])); ));
if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
&& substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)

View file

@ -8,7 +8,7 @@ use rustc_hir::{
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind, Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::AdtDef; use rustc_middle::ty::{Adt, AdtDef, SubstsRef};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym; use rustc_span::sym;
@ -81,13 +81,18 @@ fn check_struct<'tcx>(
self_ty: &Ty<'_>, self_ty: &Ty<'_>,
func_expr: &Expr<'_>, func_expr: &Expr<'_>,
adt_def: AdtDef<'_>, adt_def: AdtDef<'_>,
substs: SubstsRef<'_>,
) { ) {
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() { if let Some(PathSegment { args, .. }) = p.segments.last() {
for arg in a.args { let args = args.map(|a| a.args).unwrap_or(&[]);
if !matches!(arg, GenericArg::Lifetime(_)) {
return; // substs contains the generic parameters of the type declaration, while args contains the arguments
} // used at instantiation time. If both len are not equal, it means that some parameters were not
// provided (which means that the default values were used); in this case we will not risk
// suggesting too broad a rewrite. We won't either if any argument is a type or a const.
if substs.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
return;
} }
} }
} }
@ -184,7 +189,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
if let ImplItemKind::Fn(_, b) = &impl_item.kind; if let ImplItemKind::Fn(_, b) = &impl_item.kind;
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
if let Some(adt_def) = cx.tcx.type_of(item.owner_id).subst_identity().ty_adt_def(); if let &Adt(adt_def, substs) = cx.tcx.type_of(item.owner_id).subst_identity().kind();
if let attrs = cx.tcx.hir().attrs(item.hir_id()); if let attrs = cx.tcx.hir().attrs(item.hir_id());
if !attrs.iter().any(|attr| attr.doc_str().is_some()); if !attrs.iter().any(|attr| attr.doc_str().is_some());
if let child_attrs = cx.tcx.hir().attrs(impl_item_hir); if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
@ -192,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
then { then {
if adt_def.is_struct() { if adt_def.is_struct() {
check_struct(cx, item, self_ty, func_expr, adt_def); check_struct(cx, item, self_ty, func_expr, adt_def, substs);
} else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) {
check_enum(cx, item, func_expr, adt_def); check_enum(cx, item, func_expr, adt_def);
} }

View file

@ -11,7 +11,7 @@ declare_clippy_lint! {
/// ///
/// ### Why is this bad? /// ### Why is this bad?
/// Exit terminates the program at the location it is called. For unrecoverable /// Exit terminates the program at the location it is called. For unrecoverable
/// errors `panics` should be used to provide a stacktrace and potentualy other /// errors `panics` should be used to provide a stacktrace and potentially other
/// information. A normal termination or one with an error code should happen in /// information. A normal termination or one with an error code should happen in
/// the main function. /// the main function.
/// ///

View file

@ -25,7 +25,7 @@ declare_clippy_lint! {
/// ///
/// if fn_ptr.is_none() { ... } /// if fn_ptr.is_none() { ... }
/// ``` /// ```
#[clippy::version = "1.67.0"] #[clippy::version = "1.68.0"]
pub FN_NULL_CHECK, pub FN_NULL_CHECK,
correctness, correctness,
"`fn()` type assumed to be nullable" "`fn()` type assumed to be nullable"

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -84,9 +84,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
_ => false, _ => false,
}; };
let sugg = if is_new_string { let sugg = if is_new_string {
snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned() snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned()
} else { } else {
let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability); let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
format!("{}.to_string()", sugg.maybe_par()) format!("{}.to_string()", sugg.maybe_par())
}; };
span_useless_format(cx, call_site, sugg, applicability); span_useless_format(cx, call_site, sugg, applicability);

View file

@ -22,7 +22,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body:
if let Some(gen_span) = generics.span_for_param_suggestion() { if let Some(gen_span) = generics.span_for_param_suggestion() {
diag.span_suggestion_with_style( diag.span_suggestion_with_style(
gen_span, gen_span,
"add a type paremeter", "add a type parameter",
format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]), format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]),
rustc_errors::Applicability::HasPlaceholders, rustc_errors::Applicability::HasPlaceholders,
rustc_errors::SuggestionStyle::ShowAlways, rustc_errors::SuggestionStyle::ShowAlways,
@ -35,7 +35,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body:
ident.span.ctxt(), ident.span.ctxt(),
ident.span.parent(), ident.span.parent(),
), ),
"add a type paremeter", "add a type parameter",
format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]), format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
rustc_errors::Applicability::HasPlaceholders, rustc_errors::Applicability::HasPlaceholders,
rustc_errors::SuggestionStyle::ShowAlways, rustc_errors::SuggestionStyle::ShowAlways,

View file

@ -97,7 +97,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
let Some(correct_field) = correct_field else { let Some(correct_field) = correct_field else {
// There is no field corresponding to the getter name. // There is no field corresponding to the getter name.
// FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is // FIXME: This can be a false positive if the correct field is reachable through deeper autodereferences than used_field is
return; return;
}; };

View file

@ -185,7 +185,7 @@ declare_clippy_lint! {
/// ### Examples /// ### Examples
/// ```rust /// ```rust
/// // this could be annotated with `#[must_use]`. /// // this could be annotated with `#[must_use]`.
/// fn id<T>(t: T) -> T { t } /// pub fn id<T>(t: T) -> T { t }
/// ``` /// ```
#[clippy::version = "1.40.0"] #[clippy::version = "1.40.0"]
pub MUST_USE_CANDIDATE, pub MUST_USE_CANDIDATE,

View file

@ -1,7 +1,7 @@
use clippy_utils::consts::{constant, Constant}; use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::get_parent_expr; use clippy_utils::get_parent_expr;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_context;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::{LitIntType, LitKind}; use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -55,6 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
if let ExprKind::AssignOp(op1, target, value) = ex.kind; if let ExprKind::AssignOp(op1, target, value) = ex.kind;
let ty = cx.typeck_results().expr_ty(target); let ty = cx.typeck_results().expr_ty(target);
if Some(c) == get_int_max(ty); if Some(c) == get_int_max(ty);
let ctxt = expr.span.ctxt();
if ex.span.ctxt() == ctxt;
if expr1.span.ctxt() == ctxt;
if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target); if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target);
if BinOpKind::Add == op1.node; if BinOpKind::Add == op1.node;
if let ExprKind::Lit(ref lit) = value.kind; if let ExprKind::Lit(ref lit) = value.kind;
@ -62,8 +65,15 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
if block.expr.is_none(); if block.expr.is_none();
then { then {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let code = snippet_with_applicability(cx, target.span, "_", &mut app); let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0;
let sugg = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind && else_.hir_id == expr.hir_id {format!("{{{code} = {code}.saturating_add(1); }}")} else {format!("{code} = {code}.saturating_add(1);")}; let sugg = if let Some(parent) = get_parent_expr(cx, expr)
&& let ExprKind::If(_cond, _then, Some(else_)) = parent.kind
&& else_.hir_id == expr.hir_id
{
format!("{{{code} = {code}.saturating_add(1); }}")
} else {
format!("{code} = {code}.saturating_add(1);")
};
span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app); span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app);
} }
} }

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::{self, span_lint_and_sugg}; use clippy_utils::diagnostics::{self, span_lint_and_sugg};
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source; use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty; use clippy_utils::ty;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -161,14 +161,9 @@ fn print_unchecked_duration_subtraction_sugg(
) { ) {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let left_expr = let ctxt = expr.span.ctxt();
source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability); let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "<instant>", &mut applicability).0;
let right_expr = source::snippet_with_applicability( let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "<duration>", &mut applicability).0;
cx,
right_expr.span,
"std::time::Duration::from_secs(1)",
&mut applicability,
);
diagnostics::span_lint_and_sugg( diagnostics::span_lint_and_sugg(
cx, cx,

View file

@ -1,13 +1,14 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_context;
use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators, sugg::Sugg};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def_id::DefIdSet; use rustc_hir::def_id::DefIdSet;
use rustc_hir::{ use rustc_hir::{
def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item, def::Res, def_id::DefId, lang_items::LangItem, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg,
ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp, GenericBound, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, PathSegment, PrimTy,
QPath, TraitItemRef, TyKind, TypeBindingKind,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
@ -16,7 +17,6 @@ use rustc_span::{
source_map::{Span, Spanned, Symbol}, source_map::{Span, Spanned, Symbol},
symbol::sym, symbol::sym,
}; };
use std::borrow::Cow;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -251,33 +251,98 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum LenOutput<'tcx> { enum LenOutput {
Integral, Integral,
Option(DefId), Option(DefId),
Result(DefId, Ty<'tcx>), Result(DefId),
} }
fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
if let ty::Alias(_, alias_ty) = ty.kind() &&
let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id) &&
let Item { kind: ItemKind::OpaqueTy(opaque), .. } = item &&
opaque.bounds.len() == 1 &&
let GenericBound::LangItemTrait(LangItem::Future, _, _, generic_args) = &opaque.bounds[0] &&
generic_args.bindings.len() == 1 &&
let TypeBindingKind::Equality {
term: rustc_hir::Term::Ty(rustc_hir::Ty {kind: TyKind::Path(QPath::Resolved(_, path)), .. }),
} = &generic_args.bindings[0].kind &&
path.segments.len() == 1 {
return Some(&path.segments[0]);
}
None
}
fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool {
if let Some(generic_args) = segment.args {
if generic_args.args.is_empty() {
return false;
}
let arg = &generic_args.args[0];
if let GenericArg::Type(rustc_hir::Ty {
kind: TyKind::Path(QPath::Resolved(_, path)),
..
}) = arg
{
let segments = &path.segments;
let segment = &segments[0];
let res = &segment.res;
if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) {
return true;
}
}
}
false
}
fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<LenOutput> {
if let Some(segment) = extract_future_output(cx, sig.output()) {
let res = segment.res;
if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) {
return Some(LenOutput::Integral);
}
if let Res::Def(_, def_id) = res {
if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_first_generic_integral(segment) {
return Some(LenOutput::Option(def_id));
} else if cx.tcx.is_diagnostic_item(sym::Result, def_id) && is_first_generic_integral(segment) {
return Some(LenOutput::Result(def_id));
}
}
return None;
}
match *sig.output().kind() { match *sig.output().kind() {
ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral), ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => { ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => {
subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())) subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did()))
}, },
ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => subs ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => {
.type_at(0) subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did()))
.is_integral() },
.then(|| LenOutput::Result(adt.did(), subs.type_at(1))),
_ => None, _ => None,
} }
} }
impl<'tcx> LenOutput<'tcx> { impl LenOutput {
fn matches_is_empty_output(self, ty: Ty<'tcx>) -> bool { fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
if let Some(segment) = extract_future_output(cx, ty) {
return match (self, segment.res) {
(_, Res::PrimTy(PrimTy::Bool)) => true,
(Self::Option(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Option, def_id) => true,
(Self::Result(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Result, def_id) => true,
_ => false,
};
}
match (self, ty.kind()) { match (self, ty.kind()) {
(_, &ty::Bool) => true, (_, &ty::Bool) => true,
(Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(), (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
(Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did() => { (Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
subs.type_at(0).is_bool() && subs.type_at(1) == err_ty
},
_ => false, _ => false,
} }
} }
@ -301,9 +366,14 @@ impl<'tcx> LenOutput<'tcx> {
} }
/// Checks if the given signature matches the expectations for `is_empty` /// Checks if the given signature matches the expectations for `is_empty`
fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_output: LenOutput<'tcx>) -> bool { fn check_is_empty_sig<'tcx>(
cx: &LateContext<'tcx>,
sig: FnSig<'tcx>,
self_kind: ImplicitSelfKind,
len_output: LenOutput,
) -> bool {
match &**sig.inputs_and_output { match &**sig.inputs_and_output {
[arg, res] if len_output.matches_is_empty_output(*res) => { [arg, res] if len_output.matches_is_empty_output(cx, *res) => {
matches!( matches!(
(arg.kind(), self_kind), (arg.kind(), self_kind),
(ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef) (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef)
@ -315,11 +385,11 @@ fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_o
} }
/// Checks if the given type has an `is_empty` method with the appropriate signature. /// Checks if the given type has an `is_empty` method with the appropriate signature.
fn check_for_is_empty<'tcx>( fn check_for_is_empty(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
span: Span, span: Span,
self_kind: ImplicitSelfKind, self_kind: ImplicitSelfKind,
output: LenOutput<'tcx>, output: LenOutput,
impl_ty: DefId, impl_ty: DefId,
item_name: Symbol, item_name: Symbol,
item_kind: &str, item_kind: &str,
@ -352,6 +422,7 @@ fn check_for_is_empty<'tcx>(
Some(is_empty) Some(is_empty)
if !(is_empty.fn_has_self_parameter if !(is_empty.fn_has_self_parameter
&& check_is_empty_sig( && check_is_empty_sig(
cx,
cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(), cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(),
self_kind, self_kind,
output, output,
@ -431,7 +502,7 @@ fn check_len(
&format!("using `{op}is_empty` is clearer and more explicit"), &format!("using `{op}is_empty` is clearer and more explicit"),
format!( format!(
"{op}{}.is_empty()", "{op}{}.is_empty()",
snippet_with_applicability(cx, receiver.span, "_", &mut applicability) snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0,
), ),
applicability, applicability,
); );
@ -444,13 +515,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let lit1 = peel_ref_operators(cx, lit1); let lit1 = peel_ref_operators(cx, lit1);
let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability); let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_par();
// Wrap the expression in parentheses if it's a deref expression. Otherwise operator precedence will
// cause the code to dereference boolean(won't compile).
if let ExprKind::Unary(UnOp::Deref, _) = lit1.kind {
lit_str = Cow::from(format!("({lit_str})"));
}
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,

View file

@ -124,7 +124,7 @@ declare_clippy_lint! {
/// ``` /// ```
#[clippy::version = "1.69.0"] #[clippy::version = "1.69.0"]
pub LET_UNDERSCORE_UNTYPED, pub LET_UNDERSCORE_UNTYPED,
pedantic, restriction,
"non-binding `let` without a type annotation" "non-binding `let` without a type annotation"
} }

View file

@ -0,0 +1,45 @@
use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::*;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Detects when a variable is declared with an explicit type of `_`.
/// ### Why is this bad?
/// It adds noise, `: _` provides zero clarity or utility.
/// ### Example
/// ```rust,ignore
/// let my_number: _ = 1;
/// ```
/// Use instead:
/// ```rust,ignore
/// let my_number = 1;
/// ```
#[clippy::version = "1.69.0"]
pub LET_WITH_TYPE_UNDERSCORE,
complexity,
"unneeded underscore type (`_`) in a variable declaration"
}
declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
impl LateLintPass<'_> for UnderscoreTyped {
fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
if_chain! {
if !in_external_macro(cx.tcx.sess, local.span);
if let Some(ty) = local.ty; // Ensure that it has a type defined
if let TyKind::Infer = &ty.kind; // that type is '_'
if local.span.ctxt() == ty.span.ctxt();
then {
span_lint_and_help(cx,
LET_WITH_TYPE_UNDERSCORE,
local.span,
"variable declared with type underscore",
Some(ty.span.with_lo(local.pat.span.hi())),
"remove the explicit type `_` declaration"
)
}
};
}
}

View file

@ -87,6 +87,7 @@ mod casts;
mod checked_conversions; mod checked_conversions;
mod cognitive_complexity; mod cognitive_complexity;
mod collapsible_if; mod collapsible_if;
mod collection_is_never_read;
mod comparison_chain; mod comparison_chain;
mod copies; mod copies;
mod copy_iterator; mod copy_iterator;
@ -166,6 +167,7 @@ mod large_stack_arrays;
mod len_zero; mod len_zero;
mod let_if_seq; mod let_if_seq;
mod let_underscore; mod let_underscore;
mod let_with_type_underscore;
mod lifetimes; mod lifetimes;
mod literal_representation; mod literal_representation;
mod loops; mod loops;
@ -192,6 +194,7 @@ mod minmax;
mod misc; mod misc;
mod misc_early; mod misc_early;
mod mismatching_type_param_order; mod mismatching_type_param_order;
mod missing_assert_message;
mod missing_const_for_fn; mod missing_const_for_fn;
mod missing_doc; mod missing_doc;
mod missing_enforced_import_rename; mod missing_enforced_import_rename;
@ -249,6 +252,7 @@ mod question_mark_used;
mod ranges; mod ranges;
mod rc_clone_in_vec_init; mod rc_clone_in_vec_init;
mod read_zero_byte_vec; mod read_zero_byte_vec;
mod redundant_async_block;
mod redundant_clone; mod redundant_clone;
mod redundant_closure_call; mod redundant_closure_call;
mod redundant_else; mod redundant_else;
@ -533,6 +537,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
.collect(), .collect(),
)) ))
}); });
store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector));
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
store.register_late_pass(|_| Box::new(utils::author::Author)); store.register_late_pass(|_| Box::new(utils::author::Author));
let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
@ -924,6 +929,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
)) ))
}); });
store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)); store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock));
store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
// add lints here, do not remove this comment, it's used in `new_lint` // add lints here, do not remove this comment, it's used in `new_lint`
} }

View file

@ -1,11 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::get_parent_expr; use clippy_utils::get_parent_expr;
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_context;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym; use rustc_span::sym;
@ -55,13 +56,17 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
if_chain! { if_chain! {
if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind; if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind;
if let BinOpKind::Mul = &bin_op.node; if let BinOpKind::Mul = &bin_op.node;
if !in_external_macro(cx.sess(), expr.span);
let ctxt = expr.span.ctxt();
if left_expr.span.ctxt() == ctxt;
if right_expr.span.ctxt() == ctxt;
if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr); if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr);
if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
if let ExprKind::Lit(lit) = &other_expr.kind; if let ExprKind::Lit(lit) = &other_expr.kind;
if let LitKind::Int(8, _) = lit.node; if let LitKind::Int(8, _) = lit.node;
then { then {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app); let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0;
let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
span_lint_and_sugg( span_lint_and_sugg(

View file

@ -1,5 +1,5 @@
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, source::snippet}; use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, sugg::Sugg};
use rustc_ast::ast::RangeLimits; use rustc_ast::ast::RangeLimits;
use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::LitKind::{Byte, Char};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -115,15 +115,8 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha
CharRange::Otherwise => None, CharRange::Otherwise => None,
} { } {
let default_snip = ".."; let default_snip = "..";
// `snippet_with_applicability` may set applicability to `MaybeIncorrect` for let mut app = Applicability::MachineApplicable;
// macro span, so we check applicability manually by comparing `recv` is not default. let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par();
let recv = snippet(cx, recv.span, default_snip);
let applicability = if recv == default_snip {
Applicability::HasPlaceholders
} else {
Applicability::MachineApplicable
};
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
@ -132,7 +125,7 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha
"manual check for common ascii range", "manual check for common ascii range",
"try", "try",
format!("{recv}.{sugg}()"), format!("{recv}.{sugg}()"),
applicability, app,
); );
} }
} }

View file

@ -1,7 +1,7 @@
use clippy_utils::consts::{constant_full_int, FullInt}; use clippy_utils::consts::{constant_full_int, FullInt};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_context;
use clippy_utils::{in_constant, path_to_local}; use clippy_utils::{in_constant, path_to_local};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
@ -60,12 +60,16 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
return; return;
} }
// (x % c + c) % c
if let ExprKind::Binary(op1, expr1, right) = expr.kind if let ExprKind::Binary(op1, expr1, right) = expr.kind
&& op1.node == BinOpKind::Rem && op1.node == BinOpKind::Rem
&& let ctxt = expr.span.ctxt()
&& expr1.span.ctxt() == ctxt
&& let Some(const1) = check_for_unsigned_int_constant(cx, right) && let Some(const1) = check_for_unsigned_int_constant(cx, right)
&& let ExprKind::Binary(op2, left, right) = expr1.kind && let ExprKind::Binary(op2, left, right) = expr1.kind
&& op2.node == BinOpKind::Add && op2.node == BinOpKind::Add
&& let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right) && let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right)
&& expr2.span.ctxt() == ctxt
&& let ExprKind::Binary(op3, expr3, right) = expr2.kind && let ExprKind::Binary(op3, expr3, right) = expr2.kind
&& op3.node == BinOpKind::Rem && op3.node == BinOpKind::Rem
&& let Some(const3) = check_for_unsigned_int_constant(cx, right) && let Some(const3) = check_for_unsigned_int_constant(cx, right)
@ -86,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
}; };
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let rem_of = snippet_with_applicability(cx, expr3.span, "_", &mut app); let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0;
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
MANUAL_REM_EUCLID, MANUAL_REM_EUCLID,

View file

@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher; use clippy_utils::higher;
use clippy_utils::method_chain_args; use clippy_utils::is_res_lang_ctor;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, PatKind, QPath}; use rustc_hir::{Expr, ExprKind, LangItem, PatKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym; use rustc_span::sym;
@ -58,17 +58,18 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
}; };
if_chain! { if_chain! {
if let ExprKind::MethodCall(ok_path, result_types_0, ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _) if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation if let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind; //get operation
if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized; if ok_path.ident.as_str() == "ok";
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result); if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome);
let ctxt = expr.span.ctxt();
if let_expr.span.ctxt() == ctxt;
if let_pat.span.ctxt() == ctxt;
then { then {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability); let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0;
let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_path.ident.span), "", &mut applicability); let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0;
let sugg = format!( let sugg = format!(
"{ifwhile} let Ok({some_expr_string}) = {}", "{ifwhile} let Ok({some_expr_string}) = {}",
trimmed_ok.trim().trim_end_matches('.'), trimmed_ok.trim().trim_end_matches('.'),

View file

@ -925,7 +925,7 @@ declare_clippy_lint! {
#[clippy::version = "1.66.0"] #[clippy::version = "1.66.0"]
pub MANUAL_FILTER, pub MANUAL_FILTER,
complexity, complexity,
"reimplentation of `filter`" "reimplementation of `filter`"
} }
#[derive(Default)] #[derive(Default)]

View file

@ -340,8 +340,9 @@ declare_clippy_lint! {
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for methods with certain name prefixes and which /// Checks for methods with certain name prefixes or suffixes, and which
/// doesn't match how self is taken. The actual rules are: /// do not adhere to standard conventions regarding how `self` is taken.
/// The actual rules are:
/// ///
/// |Prefix |Postfix |`self` taken | `self` type | /// |Prefix |Postfix |`self` taken | `self` type |
/// |-------|------------|-------------------------------|--------------| /// |-------|------------|-------------------------------|--------------|

View file

@ -0,0 +1,82 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn};
use clippy_utils::{is_in_cfg_test, is_in_test_function};
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
/// Checks assertions without a custom panic message.
///
/// ### Why is this bad?
/// Without a good custom message, it'd be hard to understand what went wrong when the assertion fails.
/// A good custom message should be more about why the failure of the assertion is problematic
/// and not what is failed because the assertion already conveys that.
///
/// ### Known problems
/// This lint cannot check the quality of the custom panic messages.
/// Hence, you can suppress this lint simply by adding placeholder messages
/// like "assertion failed". However, we recommend coming up with good messages
/// that provide useful information instead of placeholder messages that
/// don't provide any extra information.
///
/// ### Example
/// ```rust
/// # struct Service { ready: bool }
/// fn call(service: Service) {
/// assert!(service.ready);
/// }
/// ```
/// Use instead:
/// ```rust
/// # struct Service { ready: bool }
/// fn call(service: Service) {
/// assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests");
/// }
/// ```
#[clippy::version = "1.69.0"]
pub MISSING_ASSERT_MESSAGE,
restriction,
"checks assertions without a custom panic message"
}
declare_lint_pass!(MissingAssertMessage => [MISSING_ASSERT_MESSAGE]);
impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
let single_argument = match cx.tcx.get_diagnostic_name(macro_call.def_id) {
Some(sym::assert_macro | sym::debug_assert_macro) => true,
Some(
sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro,
) => false,
_ => return,
};
// This lint would be very noisy in tests, so just ignore if we're in test context
if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) {
return;
}
let panic_expn = if single_argument {
let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else { return };
panic_expn
} else {
let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
panic_expn
};
if let PanicExpn::Empty = panic_expn {
span_lint_and_help(
cx,
MISSING_ASSERT_MESSAGE,
macro_call.span,
"assert without any message",
None,
"consider describing why the failing assert is problematic",
);
}
}
}

View file

@ -8,10 +8,10 @@
use clippy_utils::attrs::is_doc_hidden; use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_from_proc_macro; use clippy_utils::is_from_proc_macro;
use hir::def_id::LocalDefId;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_ast::ast::{self, MetaItem, MetaItemKind};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::Visibility; use rustc_middle::ty::Visibility;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
@ -21,8 +21,7 @@ use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Warns if there is missing doc for any documentable item /// Warns if there is missing doc for any private documentable item
/// (public or private).
/// ///
/// ### Why is this bad? /// ### Why is this bad?
/// Doc is good. *rustc* has a `MISSING_DOCS` /// Doc is good. *rustc* has a `MISSING_DOCS`
@ -32,7 +31,7 @@ declare_clippy_lint! {
#[clippy::version = "pre 1.29.0"] #[clippy::version = "pre 1.29.0"]
pub MISSING_DOCS_IN_PRIVATE_ITEMS, pub MISSING_DOCS_IN_PRIVATE_ITEMS,
restriction, restriction,
"detects missing documentation for public and private members" "detects missing documentation for private members"
} }
pub struct MissingDoc { pub struct MissingDoc {
@ -107,11 +106,14 @@ impl MissingDoc {
if vis == Visibility::Public || vis != Visibility::Restricted(CRATE_DEF_ID.into()) { if vis == Visibility::Public || vis != Visibility::Restricted(CRATE_DEF_ID.into()) {
return; return;
} }
} else if def_id != CRATE_DEF_ID && cx.effective_visibilities.is_exported(def_id) {
return;
} }
let has_doc = attrs let has_doc = attrs
.iter() .iter()
.any(|a| a.doc_str().is_some() || Self::has_include(a.meta())); .any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
if !has_doc { if !has_doc {
span_lint( span_lint(
cx, cx,

View file

@ -11,6 +11,7 @@ use rustc_ast::Mutability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span; use rustc_span::Span;
@ -120,33 +121,15 @@ fn collect_unsafe_exprs<'tcx>(
unsafe_ops.push(("raw pointer dereference occurs here", expr.span)); unsafe_ops.push(("raw pointer dereference occurs here", expr.span));
}, },
ExprKind::Call(path_expr, _) => match path_expr.kind { ExprKind::Call(path_expr, _) => {
ExprKind::Path(QPath::Resolved( let sig = match *cx.typeck_results().expr_ty(path_expr).kind() {
_, ty::FnDef(id, _) => cx.tcx.fn_sig(id).skip_binder(),
hir::Path { ty::FnPtr(sig) => sig,
res: Res::Def(kind, def_id), _ => return Continue(Descend::Yes),
.. };
}, if sig.unsafety() == Unsafety::Unsafe {
)) if kind.is_fn_like() => { unsafe_ops.push(("unsafe function call occurs here", expr.span));
let sig = cx.tcx.fn_sig(*def_id); }
if sig.0.unsafety() == Unsafety::Unsafe {
unsafe_ops.push(("unsafe function call occurs here", expr.span));
}
},
ExprKind::Path(QPath::TypeRelative(..)) => {
if let Some(sig) = cx
.typeck_results()
.type_dependent_def_id(path_expr.hir_id)
.map(|def_id| cx.tcx.fn_sig(def_id))
{
if sig.0.unsafety() == Unsafety::Unsafe {
unsafe_ops.push(("unsafe function call occurs here", expr.span));
}
}
},
_ => {},
}, },
ExprKind::MethodCall(..) => { ExprKind::MethodCall(..) => {

View file

@ -1,6 +1,6 @@
use clippy_utils::consts::{self, Constant}; use clippy_utils::consts::{self, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::sugg::has_enclosing_paren;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::util::parser::PREC_PREFIX;
@ -60,8 +60,8 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
then { then {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let snip = snippet_with_applicability(cx, exp.span, "..", &mut applicability); let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability);
let suggestion = if exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) {
format!("-({snip})") format!("-({snip})")
} else { } else {
format!("-{snip}") format!("-{snip}")

View file

@ -53,6 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
|| is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder))) || is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder)))
|| (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS)); || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS));
if let ExprKind::Lit(_) = param.kind; if let ExprKind::Lit(_) = param.kind;
if param.span.ctxt() == expr.span.ctxt();
then { then {
let Some(snip) = snippet_opt(cx, param.span) else { let Some(snip) = snippet_opt(cx, param.span) else {
@ -71,6 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE); if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE);
if let ExprKind::Lit(_) = param.kind; if let ExprKind::Lit(_) = param.kind;
if param.span.ctxt() == expr.span.ctxt();
if let Some(snip) = snippet_opt(cx, param.span); if let Some(snip) = snippet_opt(cx, param.span);
if !snip.starts_with("0o"); if !snip.starts_with("0o");
then { then {

View file

@ -143,6 +143,10 @@ impl ArithmeticSideEffects {
return; return;
} }
let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op.node {
// At least for integers, shifts are already handled by the CTFE
return;
}
let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs);
let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs);
match ( match (
@ -151,10 +155,13 @@ impl ArithmeticSideEffects {
) { ) {
(None, None) => false, (None, None) => false,
(None, Some(n)) | (Some(n), None) => match (&op.node, n) { (None, Some(n)) | (Some(n), None) => match (&op.node, n) {
(hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, // Division and module are always valid if applied to non-zero integers
(hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true,
// Addition or subtracting zeros is always a no-op
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
| (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) // Multiplication by 1 or 0 will never overflow
| (hir::BinOpKind::Mul, 0 | 1) => true, | (hir::BinOpKind::Mul, 0 | 1)
=> true,
_ => false, _ => false,
}, },
(Some(_), Some(_)) => { (Some(_), Some(_)) => {

View file

@ -21,7 +21,7 @@ declare_clippy_lint! {
/// let mut permissions = metadata.permissions(); /// let mut permissions = metadata.permissions();
/// permissions.set_readonly(false); /// permissions.set_readonly(false);
/// ``` /// ```
#[clippy::version = "1.66.0"] #[clippy::version = "1.68.0"]
pub PERMISSIONS_SET_READONLY_FALSE, pub PERMISSIONS_SET_READONLY_FALSE,
suspicious, suspicious,
"Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`" "Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`"

View file

@ -0,0 +1,84 @@
use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
use rustc_ast::ast::*;
use rustc_ast::visit::Visitor as AstVisitor;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Checks for `async` block that only returns `await` on a future.
///
/// ### Why is this bad?
/// It is simpler and more efficient to use the future directly.
///
/// ### Example
/// ```rust
/// async fn f() -> i32 {
/// 1 + 2
/// }
///
/// let fut = async {
/// f().await
/// };
/// ```
/// Use instead:
/// ```rust
/// async fn f() -> i32 {
/// 1 + 2
/// }
///
/// let fut = f();
/// ```
#[clippy::version = "1.69.0"]
pub REDUNDANT_ASYNC_BLOCK,
complexity,
"`async { future.await }` can be replaced by `future`"
}
declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]);
impl EarlyLintPass for RedundantAsyncBlock {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
if expr.span.from_expansion() {
return;
}
if let ExprKind::Async(_, _, block) = &expr.kind && block.stmts.len() == 1 &&
let Some(Stmt { kind: StmtKind::Expr(last), .. }) = block.stmts.last() &&
let ExprKind::Await(future) = &last.kind &&
!future.span.from_expansion() &&
!await_in_expr(future)
{
span_lint_and_sugg(
cx,
REDUNDANT_ASYNC_BLOCK,
expr.span,
"this async expression only awaits a single future",
"you can reduce it to",
snippet(cx, future.span, "..").into_owned(),
Applicability::MachineApplicable,
);
}
}
}
/// Check whether an expression contains `.await`
fn await_in_expr(expr: &Expr) -> bool {
let mut detector = AwaitDetector::default();
detector.visit_expr(expr);
detector.await_found
}
#[derive(Default)]
struct AwaitDetector {
await_found: bool,
}
impl<'ast> AstVisitor<'ast> for AwaitDetector {
fn visit_expr(&mut self, ex: &'ast Expr) {
match (&ex.kind, self.await_found) {
(ExprKind::Await(_), _) => self.await_found = true,
(_, false) => rustc_ast::visit::walk_expr(self, ex),
_ => (),
}
}
}

View file

@ -45,7 +45,7 @@ declare_clippy_lint! {
/// } /// }
/// } /// }
/// ``` /// ```
#[clippy::version = "1.67.0"] #[clippy::version = "1.68.0"]
pub SIZE_OF_REF, pub SIZE_OF_REF,
suspicious, suspicious,
"Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended" "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended"

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}; use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core};
@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_span::SyntaxContext;
use rustc_span::{sym, symbol::Ident, Span}; use rustc_span::{sym, symbol::Ident, Span};
declare_clippy_lint! { declare_clippy_lint! {
@ -80,43 +81,45 @@ impl<'tcx> LateLintPass<'tcx> for Swap {
} }
fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) { fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) {
let ctxt = span.ctxt();
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
if !can_mut_borrow_both(cx, e1, e2) { if !can_mut_borrow_both(cx, e1, e2) {
if let ExprKind::Index(lhs1, idx1) = e1.kind { if let ExprKind::Index(lhs1, idx1) = e1.kind
if let ExprKind::Index(lhs2, idx2) = e2.kind { && let ExprKind::Index(lhs2, idx2) = e2.kind
if eq_expr_value(cx, lhs1, lhs2) { && eq_expr_value(cx, lhs1, lhs2)
let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); && e1.span.ctxt() == ctxt
&& e2.span.ctxt() == ctxt
{
let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
if matches!(ty.kind(), ty::Slice(_)) if matches!(ty.kind(), ty::Slice(_))
|| matches!(ty.kind(), ty::Array(_, _)) || matches!(ty.kind(), ty::Array(_, _))
|| is_type_diagnostic_item(cx, ty, sym::Vec) || is_type_diagnostic_item(cx, ty, sym::Vec)
|| is_type_diagnostic_item(cx, ty, sym::VecDeque) || is_type_diagnostic_item(cx, ty, sym::VecDeque)
{ {
let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability); let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
MANUAL_SWAP, MANUAL_SWAP,
span, span,
&format!("this looks like you are swapping elements of `{slice}` manually"), &format!("this looks like you are swapping elements of `{slice}` manually"),
"try", "try",
format!( format!(
"{}.swap({}, {})", "{}.swap({}, {});",
slice.maybe_par(), slice.maybe_par(),
snippet_with_applicability(cx, idx1.span, "..", &mut applicability), snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0,
snippet_with_applicability(cx, idx2.span, "..", &mut applicability), snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0,
), ),
applicability, applicability,
); );
}
}
} }
} }
return; return;
} }
let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability); let first = Sugg::hir_with_context(cx, e1, ctxt, "..", &mut applicability);
let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability); let second = Sugg::hir_with_context(cx, e2, ctxt, "..", &mut applicability);
let Some(sugg) = std_or_core(cx) else { return }; let Some(sugg) = std_or_core(cx) else { return };
span_lint_and_then( span_lint_and_then(
@ -128,7 +131,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
diag.span_suggestion( diag.span_suggestion(
span, span,
"try", "try",
format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()), format!("{sugg}::mem::swap({}, {});", first.mut_addr(), second.mut_addr()),
applicability, applicability,
); );
if !is_xor_based { if !is_xor_based {
@ -144,19 +147,19 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
return; return;
} }
for w in block.stmts.windows(3) { for [s1, s2, s3] in block.stmts.array_windows::<3>() {
if_chain! { if_chain! {
// let t = foo(); // let t = foo();
if let StmtKind::Local(tmp) = w[0].kind; if let StmtKind::Local(tmp) = s1.kind;
if let Some(tmp_init) = tmp.init; if let Some(tmp_init) = tmp.init;
if let PatKind::Binding(.., ident, None) = tmp.pat.kind; if let PatKind::Binding(.., ident, None) = tmp.pat.kind;
// foo() = bar(); // foo() = bar();
if let StmtKind::Semi(first) = w[1].kind; if let StmtKind::Semi(first) = s2.kind;
if let ExprKind::Assign(lhs1, rhs1, _) = first.kind; if let ExprKind::Assign(lhs1, rhs1, _) = first.kind;
// bar() = t; // bar() = t;
if let StmtKind::Semi(second) = w[2].kind; if let StmtKind::Semi(second) = s3.kind;
if let ExprKind::Assign(lhs2, rhs2, _) = second.kind; if let ExprKind::Assign(lhs2, rhs2, _) = second.kind;
if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind; if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind;
if rhs2.segments.len() == 1; if rhs2.segments.len() == 1;
@ -164,8 +167,15 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
if ident.name == rhs2.segments[0].ident.name; if ident.name == rhs2.segments[0].ident.name;
if eq_expr_value(cx, tmp_init, lhs1); if eq_expr_value(cx, tmp_init, lhs1);
if eq_expr_value(cx, rhs1, lhs2); if eq_expr_value(cx, rhs1, lhs2);
let ctxt = s1.span.ctxt();
if s2.span.ctxt() == ctxt;
if s3.span.ctxt() == ctxt;
if first.span.ctxt() == ctxt;
if second.span.ctxt() == ctxt;
then { then {
let span = w[0].span.to(second.span); let span = s1.span.to(s3.span);
generate_swap_warning(cx, lhs1, lhs2, span, false); generate_swap_warning(cx, lhs1, lhs2, span, false);
} }
} }
@ -246,17 +256,20 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr<
/// Implementation of the xor case for `MANUAL_SWAP` lint. /// Implementation of the xor case for `MANUAL_SWAP` lint.
fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) { fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
for window in block.stmts.windows(3) { for [s1, s2, s3] in block.stmts.array_windows::<3>() {
if_chain! { if_chain! {
if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(&window[0]); let ctxt = s1.span.ctxt();
if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(&window[1]); if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt);
if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(&window[2]); if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt);
if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt);
if eq_expr_value(cx, lhs0, rhs1); if eq_expr_value(cx, lhs0, rhs1);
if eq_expr_value(cx, lhs2, rhs1); if eq_expr_value(cx, lhs2, rhs1);
if eq_expr_value(cx, lhs1, rhs0); if eq_expr_value(cx, lhs1, rhs0);
if eq_expr_value(cx, lhs1, rhs2); if eq_expr_value(cx, lhs1, rhs2);
if s2.span.ctxt() == ctxt;
if s3.span.ctxt() == ctxt;
then { then {
let span = window[0].span.to(window[2].span); let span = s1.span.to(s3.span);
generate_swap_warning(cx, lhs0, rhs0, span, true); generate_swap_warning(cx, lhs0, rhs0, span, true);
} }
}; };
@ -264,9 +277,12 @@ fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
} }
/// Returns the lhs and rhs of an xor assignment statement. /// Returns the lhs and rhs of an xor assignment statement.
fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> { fn extract_sides_of_xor_assign<'a, 'hir>(
if let StmtKind::Semi(expr) = stmt.kind { stmt: &'a Stmt<'hir>,
if let ExprKind::AssignOp( ctxt: SyntaxContext,
) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
if let StmtKind::Semi(expr) = stmt.kind
&& let ExprKind::AssignOp(
Spanned { Spanned {
node: BinOpKind::BitXor, node: BinOpKind::BitXor,
.. ..
@ -274,9 +290,10 @@ fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Ex
lhs, lhs,
rhs, rhs,
) = expr.kind ) = expr.kind
{ && expr.span.ctxt() == ctxt
return Some((lhs, rhs)); {
} Some((lhs, rhs))
} else {
None
} }
None
} }

View file

@ -458,7 +458,7 @@ declare_clippy_lint! {
/// ```rust /// ```rust
/// let null_fn: Option<fn()> = None; /// let null_fn: Option<fn()> = None;
/// ``` /// ```
#[clippy::version = "1.67.0"] #[clippy::version = "1.68.0"]
pub TRANSMUTE_NULL_TO_FN, pub TRANSMUTE_NULL_TO_FN,
correctness, correctness,
"transmute results in a null function pointer, which is undefined behavior" "transmute results in a null function pointer, which is undefined behavior"

View file

@ -5,7 +5,7 @@ use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, Node, PatKind, QPath, TyKind}; use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind};
use rustc_lint::{LateContext, LintContext}; use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty; use rustc_middle::ty;
@ -41,6 +41,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
); );
} }
} else { } else {
if let ExprKind::Match(_, _, MatchSource::AwaitDesugar) = init.kind {
return
}
span_lint_and_then( span_lint_and_then(
cx, cx,
LET_UNIT_VALUE, LET_UNIT_VALUE,

View file

@ -10,8 +10,8 @@ use rustc_hir::{
def::{CtorOf, DefKind, Res}, def::{CtorOf, DefKind, Res},
def_id::LocalDefId, def_id::LocalDefId,
intravisit::{walk_inf, walk_ty, Visitor}, intravisit::{walk_inf, walk_ty, Visitor},
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item,
TyKind, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
}; };
use rustc_hir_analysis::hir_ty_to_ty; use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
// avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
// we're in an `impl` or nested item, that we don't want to lint // we're in an `impl` or nested item, that we don't want to lint
let stack_item = if_chain! { let stack_item = if_chain! {
if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind; if let ItemKind::Impl(Impl { self_ty, generics,.. }) = item.kind;
if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind; if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
if parameters.as_ref().map_or(true, |params| { if parameters.as_ref().map_or(true, |params| {
@ -105,10 +105,17 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
if !item.span.from_expansion(); if !item.span.from_expansion();
if !is_from_proc_macro(cx, item); // expensive, should be last check if !is_from_proc_macro(cx, item); // expensive, should be last check
then { then {
// Self cannot be used inside const generic parameters
let types_to_skip = generics.params.iter().filter_map(|param| {
match param {
GenericParam { kind: GenericParamKind::Const { ty: Ty { hir_id, ..}, ..}, ..} => Some(*hir_id),
_ => None,
}
}).chain(std::iter::once(self_ty.hir_id)).collect();
StackItem::Check { StackItem::Check {
impl_id: item.owner_id.def_id, impl_id: item.owner_id.def_id,
in_body: 0, in_body: 0,
types_to_skip: std::iter::once(self_ty.hir_id).collect(), types_to_skip,
} }
} else { } else {
StackItem::NoCheck StackItem::NoCheck

View file

@ -588,7 +588,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
}, },
} }
}, },
ExprKind::Err(_) => kind!("Err"), ExprKind::Err(_) => kind!("Err(_)"),
ExprKind::DropTemps(expr) => { ExprKind::DropTemps(expr) => {
bind!(self, expr); bind!(self, expr);
kind!("DropTemps({expr})"); kind!("DropTemps({expr})");

View file

@ -0,0 +1,23 @@
use clippy_utils::macros::collect_ast_format_args;
use rustc_ast::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Collects [`rustc_ast::FormatArgs`] so that future late passes can call
/// [`clippy_utils::macros::find_format_args`]
pub FORMAT_ARGS_COLLECTOR,
internal_warn,
"collects `format_args` AST nodes for use in later lints"
}
declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
impl EarlyLintPass for FormatArgsCollector {
fn check_expr(&mut self, _: &EarlyContext<'_>, expr: &Expr) {
if let ExprKind::FormatArgs(args) = &expr.kind {
collect_ast_format_args(expr.span, args);
}
}
}

View file

@ -26,7 +26,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::{sym, Loc, Span, Symbol}; use rustc_span::{sym, Loc, Span, Symbol};
use serde::{ser::SerializeStruct, Serialize, Serializer}; use serde::{ser::SerializeStruct, Serialize, Serializer};
use std::collections::BinaryHeap; use std::collections::{BTreeSet, BinaryHeap};
use std::fmt; use std::fmt;
use std::fmt::Write as _; use std::fmt::Write as _;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
@ -264,6 +264,9 @@ struct LintMetadata {
/// This field is only used in the output and will only be /// This field is only used in the output and will only be
/// mapped shortly before the actual output. /// mapped shortly before the actual output.
applicability: Option<ApplicabilityInfo>, applicability: Option<ApplicabilityInfo>,
/// All the past names of lints which have been renamed.
#[serde(skip_serializing_if = "BTreeSet::is_empty")]
former_ids: BTreeSet<String>,
} }
impl LintMetadata { impl LintMetadata {
@ -283,6 +286,7 @@ impl LintMetadata {
version, version,
docs, docs,
applicability: None, applicability: None,
former_ids: BTreeSet::new(),
} }
} }
} }
@ -901,6 +905,7 @@ fn collect_renames(lints: &mut Vec<LintMetadata>) {
if name == lint_name; if name == lint_name;
if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
then { then {
lint.former_ids.insert(past_name.to_owned());
writeln!(collected, "* `{past_name}`").unwrap(); writeln!(collected, "* `{past_name}`").unwrap();
names.push(past_name.to_string()); names.push(past_name.to_string());
} }

View file

@ -1,5 +1,6 @@
pub mod author; pub mod author;
pub mod conf; pub mod conf;
pub mod dump_hir; pub mod dump_hir;
pub mod format_args_collector;
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
pub mod internal_lints; pub mod internal_lints;

View file

@ -1,10 +1,11 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall}; use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall};
use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
use clippy_utils::{is_in_cfg_test, is_in_test_function}; use clippy_utils::{is_in_cfg_test, is_in_test_function};
use rustc_ast::LitKind; use rustc_ast::token::LitKind;
use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind}; use rustc_hir::{Expr, Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, BytePos}; use rustc_span::{sym, BytePos};
@ -297,34 +298,40 @@ impl<'tcx> LateLintPass<'tcx> for Write {
_ => return, _ => return,
} }
let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return }; find_format_args(cx, expr, macro_call.expn, |format_args| {
// ignore `writeln!(w)` and `write!(v, some_macro!())`
if format_args.span.from_expansion() {
return;
}
// ignore `writeln!(w)` and `write!(v, some_macro!())` match diag_name {
if format_args.format_string.span.from_expansion() { sym::print_macro | sym::eprint_macro | sym::write_macro => {
return; check_newline(cx, format_args, &macro_call, name);
} },
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
check_empty_string(cx, format_args, &macro_call, name);
},
_ => {},
}
match diag_name { check_literal(cx, format_args, name);
sym::print_macro | sym::eprint_macro | sym::write_macro => {
check_newline(cx, &format_args, &macro_call, name);
},
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
check_empty_string(cx, &format_args, &macro_call, name);
},
_ => {},
}
check_literal(cx, &format_args, name); if !self.in_debug_impl {
for piece in &format_args.template {
if !self.in_debug_impl { if let &FormatArgsPiece::Placeholder(FormatPlaceholder {
for arg in &format_args.args { span: Some(span),
if arg.format.r#trait == sym::Debug { format_trait: FormatTrait::Debug,
span_lint(cx, USE_DEBUG, arg.span, "use of `Debug`-based formatting"); ..
}) = piece
{
span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
}
} }
} }
} });
} }
} }
fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind
&& let Some(trait_id) = trait_ref.trait_def_id() && let Some(trait_id) = trait_ref.trait_def_id()
@ -335,16 +342,18 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
} }
} }
fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) { fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
let format_string_parts = &format_args.format_string.parts; let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { return };
let mut format_string_span = format_args.format_string.span;
let Some(last) = format_string_parts.last() else { return };
let count_vertical_whitespace = || { let count_vertical_whitespace = || {
format_string_parts format_args
.template
.iter() .iter()
.flat_map(|part| part.as_str().chars()) .filter_map(|piece| match piece {
FormatArgsPiece::Literal(literal) => Some(literal),
FormatArgsPiece::Placeholder(_) => None,
})
.flat_map(|literal| literal.as_str().chars())
.filter(|ch| matches!(ch, '\r' | '\n')) .filter(|ch| matches!(ch, '\r' | '\n'))
.count() .count()
}; };
@ -352,10 +361,9 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
if last.as_str().ends_with('\n') if last.as_str().ends_with('\n')
// ignore format strings with other internal vertical whitespace // ignore format strings with other internal vertical whitespace
&& count_vertical_whitespace() == 1 && count_vertical_whitespace() == 1
// ignore trailing arguments: `print!("Issue\n{}", 1265);`
&& format_string_parts.len() > format_args.args.len()
{ {
let mut format_string_span = format_args.span;
let lint = if name == "write" { let lint = if name == "write" {
format_string_span = expand_past_previous_comma(cx, format_string_span); format_string_span = expand_past_previous_comma(cx, format_string_span);
@ -373,7 +381,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return }; let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return };
if format_string_parts.len() == 1 && last.as_str() == "\n" { if format_args.template.len() == 1 && last.as_str() == "\n" {
// print!("\n"), write!(f, "\n") // print!("\n"), write!(f, "\n")
diag.multipart_suggestion( diag.multipart_suggestion(
@ -398,11 +406,12 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
} }
} }
fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) { fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
if let [part] = &format_args.format_string.parts[..] if let [FormatArgsPiece::Literal(literal)] = &format_args.template[..]
&& let mut span = format_args.format_string.span && literal.as_str() == "\n"
&& part.as_str() == "\n"
{ {
let mut span = format_args.span;
let lint = if name == "writeln" { let lint = if name == "writeln" {
span = expand_past_previous_comma(cx, span); span = expand_past_previous_comma(cx, span);
@ -428,33 +437,43 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, ma
} }
} }
fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) { fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
let mut counts = HirIdMap::<usize>::default(); let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos);
for param in format_args.params() {
*counts.entry(param.value.hir_id).or_default() += 1; let mut counts = vec![0u32; format_args.arguments.all_args().len()];
for piece in &format_args.template {
if let FormatArgsPiece::Placeholder(placeholder) = piece {
counts[arg_index(&placeholder.argument)] += 1;
}
} }
for arg in &format_args.args { for piece in &format_args.template {
let value = arg.param.value; if let FormatArgsPiece::Placeholder(FormatPlaceholder {
argument,
if counts[&value.hir_id] == 1 span: Some(placeholder_span),
&& arg.format.is_default() format_trait: FormatTrait::Display,
&& let ExprKind::Lit(lit) = &value.kind format_options,
&& !value.span.from_expansion() }) = piece
&& let Some(value_string) = snippet_opt(cx, value.span) && *format_options == FormatOptions::default()
{ && let index = arg_index(argument)
let (replacement, replace_raw) = match lit.node { && counts[index] == 1
LitKind::Str(..) => extract_str_literal(&value_string), && let Some(arg) = format_args.arguments.by_index(index)
LitKind::Char(ch) => ( && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
match ch { && !arg.expr.span.from_expansion()
'"' => "\\\"", && let Some(value_string) = snippet_opt(cx, arg.expr.span)
'\'' => "'", {
let (replacement, replace_raw) = match lit.kind {
LitKind::Str | LitKind::StrRaw(_) => extract_str_literal(&value_string),
LitKind::Char => (
match lit.symbol.as_str() {
"\"" => "\\\"",
"\\'" => "'",
_ => &value_string[1..value_string.len() - 1], _ => &value_string[1..value_string.len() - 1],
} }
.to_string(), .to_string(),
false, false,
), ),
LitKind::Bool(b) => (b.to_string(), false), LitKind::Bool => (lit.symbol.to_string(), false),
_ => continue, _ => continue,
}; };
@ -464,7 +483,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &
PRINT_LITERAL PRINT_LITERAL
}; };
let format_string_is_raw = format_args.format_string.style.is_some(); let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { continue };
let format_string_is_raw = format_string_snippet.starts_with('r');
let replacement = match (format_string_is_raw, replace_raw) { let replacement = match (format_string_is_raw, replace_raw) {
(false, false) => Some(replacement), (false, false) => Some(replacement),
(false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")), (false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")),
@ -485,23 +506,24 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &
span_lint_and_then( span_lint_and_then(
cx, cx,
lint, lint,
value.span, arg.expr.span,
"literal with an empty format string", "literal with an empty format string",
|diag| { |diag| {
if let Some(replacement) = replacement if let Some(replacement) = replacement
// `format!("{}", "a")`, `format!("{named}", named = "b") // `format!("{}", "a")`, `format!("{named}", named = "b")
// ~~~~~ ~~~~~~~~~~~~~ // ~~~~~ ~~~~~~~~~~~~~
&& let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id) && let Some(removal_span) = format_arg_removal_span(format_args, index)
{ {
let replacement = replacement.replace('{', "{{").replace('}', "}}"); let replacement = replacement.replace('{', "{{").replace('}', "}}");
diag.multipart_suggestion( diag.multipart_suggestion(
"try this", "try this",
vec![(arg.span, replacement), (value_span, String::new())], vec![(*placeholder_span, replacement), (removal_span, String::new())],
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
}, },
); );
} }
} }
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_utils" name = "clippy_utils"
version = "0.1.69" version = "0.1.70"
edition = "2021" edition = "2021"
publish = false publish = false

View file

@ -617,7 +617,7 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Re
/// Can return multiple resolutions when there are multiple versions of the same crate, e.g. /// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0. /// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
/// ///
/// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec` /// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec`
/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. /// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
/// ///
/// This function is expensive and should be used sparingly. /// This function is expensive and should be used sparingly.

View file

@ -6,6 +6,8 @@ use crate::visitors::{for_each_expr, Descend};
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use itertools::{izip, Either, Itertools}; use itertools::{izip, Either, Itertools};
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_ast::FormatArgs;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind}; use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind};
use rustc_lexer::unescape::unescape_literal; use rustc_lexer::unescape::unescape_literal;
@ -15,8 +17,10 @@ use rustc_parse_format::{self as rpf, Alignment};
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol}; use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
use std::cell::RefCell;
use std::iter::{once, zip}; use std::iter::{once, zip};
use std::ops::ControlFlow; use std::ops::ControlFlow;
use std::sync::atomic::{AtomicBool, Ordering};
const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
sym::assert_eq_macro, sym::assert_eq_macro,
@ -213,6 +217,7 @@ pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool {
matches!(name, sym::assert_macro | sym::debug_assert_macro) matches!(name, sym::assert_macro | sym::debug_assert_macro)
} }
#[derive(Debug)]
pub enum PanicExpn<'a> { pub enum PanicExpn<'a> {
/// No arguments - `panic!()` /// No arguments - `panic!()`
Empty, Empty,
@ -226,10 +231,7 @@ pub enum PanicExpn<'a> {
impl<'a> PanicExpn<'a> { impl<'a> PanicExpn<'a> {
pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> { pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) { let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else { return None };
return None;
}
let ExprKind::Call(callee, [arg]) = &expr.kind else { return None };
let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None }; let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
let result = match path.segments.last().unwrap().ident.as_str() { let result = match path.segments.last().unwrap().ident.as_str() {
"panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty, "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
@ -239,6 +241,21 @@ impl<'a> PanicExpn<'a> {
Self::Display(e) Self::Display(e)
}, },
"panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?), "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
// Since Rust 1.52, `assert_{eq,ne}` macros expand to use:
// `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));`
"assert_failed" => {
// It should have 4 arguments in total (we already matched with the first argument,
// so we're just checking for 3)
if rest.len() != 3 {
return None;
}
// `msg_arg` is either `None` (no custom message) or `Some(format_args!(..))` (custom message)
let msg_arg = &rest[2];
match msg_arg.kind {
ExprKind::Call(_, [fmt_arg]) => Self::Format(FormatArgsExpn::parse(cx, fmt_arg)?),
_ => Self::Empty,
}
},
_ => return None, _ => return None,
}; };
Some(result) Some(result)
@ -251,7 +268,17 @@ pub fn find_assert_args<'a>(
expr: &'a Expr<'a>, expr: &'a Expr<'a>,
expn: ExpnId, expn: ExpnId,
) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> { ) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p)) find_assert_args_inner(cx, expr, expn).map(|([e], mut p)| {
// `assert!(..)` expands to `core::panicking::panic("assertion failed: ...")` (which we map to
// `PanicExpn::Str(..)`) and `assert!(.., "..")` expands to
// `core::panicking::panic_fmt(format_args!(".."))` (which we map to `PanicExpn::Format(..)`).
// So even we got `PanicExpn::Str(..)` that means there is no custom message provided
if let PanicExpn::Str(_) = p {
p = PanicExpn::Empty;
}
(e, p)
})
} }
/// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro /// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
@ -275,13 +302,12 @@ fn find_assert_args_inner<'a, const N: usize>(
Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?, Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
}; };
let mut args = ArrayVec::new(); let mut args = ArrayVec::new();
let mut panic_expn = None; let panic_expn = for_each_expr(expr, |e| {
let _: Option<!> = for_each_expr(expr, |e| {
if args.is_full() { if args.is_full() {
if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() { match PanicExpn::parse(cx, e) {
panic_expn = PanicExpn::parse(cx, e); Some(expn) => ControlFlow::Break(expn),
None => ControlFlow::Continue(Descend::Yes),
} }
ControlFlow::Continue(Descend::from(panic_expn.is_none()))
} else if is_assert_arg(cx, e, expn) { } else if is_assert_arg(cx, e, expn) {
args.push(e); args.push(e);
ControlFlow::Continue(Descend::No) ControlFlow::Continue(Descend::No)
@ -339,6 +365,77 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) ->
} }
} }
thread_local! {
/// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be
/// able to access the many features of a [`LateContext`].
///
/// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
/// assumption that the early pass the populates the map and the later late passes will all be
/// running on the same thread.
static AST_FORMAT_ARGS: RefCell<FxHashMap<Span, FormatArgs>> = {
static CALLED: AtomicBool = AtomicBool::new(false);
debug_assert!(
!CALLED.swap(true, Ordering::SeqCst),
"incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
);
RefCell::default()
};
}
/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by
/// `FormatArgsCollector`
pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) {
AST_FORMAT_ARGS.with(|ast_format_args| {
ast_format_args.borrow_mut().insert(span, format_args.clone());
});
}
/// Calls `callback` with an AST [`FormatArgs`] node if one is found
pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) {
let format_args_expr = for_each_expr(start, |expr| {
let ctxt = expr.span.ctxt();
if ctxt == start.span.ctxt() {
ControlFlow::Continue(Descend::Yes)
} else if ctxt.outer_expn().is_descendant_of(expn_id)
&& macro_backtrace(expr.span)
.map(|macro_call| cx.tcx.item_name(macro_call.def_id))
.any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))
{
ControlFlow::Break(expr)
} else {
ControlFlow::Continue(Descend::No)
}
});
if let Some(format_args_expr) = format_args_expr {
AST_FORMAT_ARGS.with(|ast_format_args| {
ast_format_args.borrow().get(&format_args_expr.span).map(callback);
});
}
}
/// Returns the [`Span`] of the value at `index` extended to the previous comma, e.g. for the value
/// `10`
///
/// ```ignore
/// format("{}.{}", 10, 11)
/// // ^^^^
/// ```
pub fn format_arg_removal_span(format_args: &FormatArgs, index: usize) -> Option<Span> {
let ctxt = format_args.span.ctxt();
let current = hygiene::walk_chain(format_args.arguments.by_index(index)?.expr.span, ctxt);
let prev = if index == 0 {
format_args.span
} else {
hygiene::walk_chain(format_args.arguments.by_index(index - 1)?.expr.span, ctxt)
};
Some(current.with_lo(prev.hi()))
}
/// The format string doesn't exist in the HIR, so we reassemble it from source code /// The format string doesn't exist in the HIR, so we reassemble it from source code
#[derive(Debug)] #[derive(Debug)]
pub struct FormatString { pub struct FormatString {

View file

@ -16,9 +16,9 @@ use rustc_infer::infer::{
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::mir::interpret::{ConstValue, Scalar};
use rustc_middle::ty::{ use rustc_middle::ty::{
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate, self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind,
PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
TypeVisitor, UintTy, VariantDef, VariantDiscr, UintTy, VariantDef, VariantDiscr,
}; };
use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_middle::ty::{GenericArg, GenericArgKind};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;

View file

@ -1,6 +1,6 @@
[package] [package]
name = "declare_clippy_lint" name = "declare_clippy_lint"
version = "0.1.69" version = "0.1.70"
edition = "2021" edition = "2021"
publish = false publish = false

View file

@ -8,12 +8,16 @@ repository = "https://github.com/rust-lang/rust-clippy"
categories = ["development-tools"] categories = ["development-tools"]
edition = "2021" edition = "2021"
publish = false publish = false
default-run = "lintcheck"
[dependencies] [dependencies]
anyhow = "1.0.69"
cargo_metadata = "0.15.3" cargo_metadata = "0.15.3"
clap = "4.1.4" clap = { version = "4.1.8", features = ["derive", "env"] }
crates_io_api = "0.8.1"
crossbeam-channel = "0.5.6" crossbeam-channel = "0.5.6"
flate2 = "1.0" flate2 = "1.0"
indicatif = "0.17.3"
rayon = "1.5.1" rayon = "1.5.1"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.85" serde_json = "1.0.85"
@ -24,3 +28,11 @@ walkdir = "2.3"
[features] [features]
deny-warnings = [] deny-warnings = []
[[bin]]
name = "lintcheck"
path = "src/main.rs"
[[bin]]
name = "popular-crates"
path = "src/popular-crates.rs"

View file

@ -25,6 +25,15 @@ the repo root.
The results will then be saved to `lintcheck-logs/custom_logs.toml`. The results will then be saved to `lintcheck-logs/custom_logs.toml`.
The `custom.toml` file may be built using <https://crates.io> recently most
downloaded crates by using the `popular-crates` binary from the `lintcheck`
directory. For example, to retrieve the 100 recently most downloaded crates:
```
cargo run --release --bin popular-crates -- -n 100 custom.toml
```
### Configuring the Crate Sources ### Configuring the Crate Sources
The sources to check are saved in a `toml` file. There are three types of The sources to check are saved in a `toml` file. There are three types of

View file

@ -1,131 +1,79 @@
use clap::{Arg, ArgAction, ArgMatches, Command}; use clap::Parser;
use std::env; use std::{num::NonZeroUsize, path::PathBuf};
use std::path::PathBuf;
fn get_clap_config() -> ArgMatches { #[derive(Clone, Debug, Parser)]
Command::new("lintcheck")
.about("run clippy on a set of crates and check output")
.args([
Arg::new("only")
.action(ArgAction::Set)
.value_name("CRATE")
.long("only")
.help("Only process a single crate of the list"),
Arg::new("crates-toml")
.action(ArgAction::Set)
.value_name("CRATES-SOURCES-TOML-PATH")
.long("crates-toml")
.help("Set the path for a crates.toml where lintcheck should read the sources from"),
Arg::new("threads")
.action(ArgAction::Set)
.value_name("N")
.value_parser(clap::value_parser!(usize))
.short('j')
.long("jobs")
.help("Number of threads to use, 0 automatic choice"),
Arg::new("fix")
.long("fix")
.help("Runs cargo clippy --fix and checks if all suggestions apply"),
Arg::new("filter")
.long("filter")
.action(ArgAction::Append)
.value_name("clippy_lint_name")
.help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"),
Arg::new("markdown")
.long("markdown")
.help("Change the reports table to use markdown links"),
Arg::new("recursive")
.long("recursive")
.help("Run clippy on the dependencies of crates specified in crates-toml")
.conflicts_with("threads")
.conflicts_with("fix"),
])
.get_matches()
}
#[derive(Debug, Clone)]
pub(crate) struct LintcheckConfig { pub(crate) struct LintcheckConfig {
/// max number of jobs to spawn (default 1) /// Number of threads to use (default: all unless --fix or --recursive)
#[clap(
long = "jobs",
short = 'j',
value_name = "N",
default_value_t = 0,
hide_default_value = true
)]
pub max_jobs: usize, pub max_jobs: usize,
/// we read the sources to check from here /// Set the path for a crates.toml where lintcheck should read the sources from
#[clap(
long = "crates-toml",
value_name = "CRATES-SOURCES-TOML-PATH",
default_value = "lintcheck/lintcheck_crates.toml",
hide_default_value = true,
env = "LINTCHECK_TOML",
hide_env = true
)]
pub sources_toml_path: PathBuf, pub sources_toml_path: PathBuf,
/// we save the clippy lint results here /// File to save the clippy lint results here
pub lintcheck_results_path: PathBuf, #[clap(skip = "")]
/// Check only a specified package pub lintcheck_results_path: PathBuf, // Overridden in new()
/// Only process a single crate on the list
#[clap(long, value_name = "CRATE")]
pub only: Option<String>, pub only: Option<String>,
/// whether to just run --fix and not collect all the warnings /// Runs cargo clippy --fix and checks if all suggestions apply
#[clap(long, conflicts_with("max_jobs"))]
pub fix: bool, pub fix: bool,
/// A list of lints that this lintcheck run should focus on /// Apply a filter to only collect specified lints, this also overrides `allow` attributes
#[clap(long = "filter", value_name = "clippy_lint_name", use_value_delimiter = true)]
pub lint_filter: Vec<String>, pub lint_filter: Vec<String>,
/// Indicate if the output should support markdown syntax /// Change the reports table to use markdown links
#[clap(long)]
pub markdown: bool, pub markdown: bool,
/// Run clippy on the dependencies of crates /// Run clippy on the dependencies of crates specified in crates-toml
#[clap(long, conflicts_with("max_jobs"))]
pub recursive: bool, pub recursive: bool,
} }
impl LintcheckConfig { impl LintcheckConfig {
pub fn new() -> Self { pub fn new() -> Self {
let clap_config = get_clap_config(); let mut config = LintcheckConfig::parse();
// first, check if we got anything passed via the LINTCHECK_TOML env var,
// if not, ask clap if we got any value for --crates-toml <foo>
// if not, use the default "lintcheck/lintcheck_crates.toml"
let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| {
clap_config
.get_one::<String>("crates-toml")
.map_or("lintcheck/lintcheck_crates.toml", |s| &**s)
.into()
});
let markdown = clap_config.contains_id("markdown");
let sources_toml_path = PathBuf::from(sources_toml);
// for the path where we save the lint results, get the filename without extension (so for // for the path where we save the lint results, get the filename without extension (so for
// wasd.toml, use "wasd"...) // wasd.toml, use "wasd"...)
let filename: PathBuf = sources_toml_path.file_stem().unwrap().into(); let filename: PathBuf = config.sources_toml_path.file_stem().unwrap().into();
let lintcheck_results_path = PathBuf::from(format!( config.lintcheck_results_path = PathBuf::from(format!(
"lintcheck-logs/{}_logs.{}", "lintcheck-logs/{}_logs.{}",
filename.display(), filename.display(),
if markdown { "md" } else { "txt" } if config.markdown { "md" } else { "txt" }
)); ));
// look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and // look at the --threads arg, if 0 is passed, use the threads count
// use half of that for the physical core count if config.max_jobs == 0 {
// by default use a single thread config.max_jobs = if config.fix || config.recursive {
let max_jobs = match clap_config.get_one::<usize>("threads") { 1
Some(&0) => { } else {
// automatic choice std::thread::available_parallelism().map_or(1, NonZeroUsize::get)
// Rayon seems to return thread count so half that for core count };
rayon::current_num_threads() / 2
},
Some(&threads) => threads,
// no -j passed, use a single thread
None => 1,
}; };
let lint_filter: Vec<String> = clap_config for lint_name in &mut config.lint_filter {
.get_many::<String>("filter") *lint_name = format!(
.map(|iter| { "clippy::{}",
iter.map(|lint_name| { lint_name
let mut filter = lint_name.replace('_', "-"); .strip_prefix("clippy::")
if !filter.starts_with("clippy::") { .unwrap_or(lint_name)
filter.insert_str(0, "clippy::"); .replace('_', "-")
} );
filter
})
.collect()
})
.unwrap_or_default();
LintcheckConfig {
max_jobs,
sources_toml_path,
lintcheck_results_path,
only: clap_config.get_one::<String>("only").map(String::from),
fix: clap_config.contains_id("fix"),
lint_filter,
markdown,
recursive: clap_config.contains_id("recursive"),
} }
config
} }
} }

View file

@ -0,0 +1,65 @@
#![deny(clippy::pedantic)]
use clap::Parser;
use crates_io_api::{CratesQueryBuilder, Sort, SyncClient};
use indicatif::ProgressBar;
use std::collections::HashSet;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::PathBuf;
use std::time::Duration;
#[derive(Parser)]
struct Opts {
/// Output TOML file name
output: PathBuf,
/// Number of crate names to download
#[clap(short, long, default_value_t = 100)]
number: usize,
/// Do not output progress
#[clap(short, long)]
quiet: bool,
}
fn main() -> anyhow::Result<()> {
let opts = Opts::parse();
let mut output = BufWriter::new(File::create(opts.output)?);
output.write_all(b"[crates]\n")?;
let client = SyncClient::new(
"clippy/lintcheck (github.com/rust-lang/rust-clippy/)",
Duration::from_secs(1),
)?;
let mut seen_crates = HashSet::new();
let pb = if opts.quiet {
None
} else {
Some(ProgressBar::new(opts.number as u64))
};
let mut query = CratesQueryBuilder::new()
.sort(Sort::RecentDownloads)
.page_size(100)
.build();
while seen_crates.len() < opts.number {
let retrieved = client.crates(query.clone())?.crates;
if retrieved.is_empty() {
eprintln!("No more than {} crates available from API", seen_crates.len());
break;
}
for c in retrieved {
if seen_crates.insert(c.name.clone()) {
output.write_all(
format!(
"{} = {{ name = '{}', versions = ['{}'] }}\n",
c.name, c.name, c.max_version
)
.as_bytes(),
)?;
if let Some(pb) = &pb {
pb.inc(1);
}
}
}
query.set_page(query.page() + 1);
}
Ok(())
}

View file

@ -1,3 +1,3 @@
[toolchain] [toolchain]
channel = "nightly-2023-02-25" channel = "nightly-2023-03-10"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]

View file

@ -176,7 +176,7 @@ Common options:
--rustc Pass all args to rustc --rustc Pass all args to rustc
-V, --version Print version info and exit -V, --version Print version info and exit
Other options are the same as `cargo check`. For the other options see `cargo check --help`.
To allow or deny a lint from the command line you can use `cargo clippy --` To allow or deny a lint from the command line you can use `cargo clippy --`
with: with:

View file

@ -18,7 +18,7 @@ Common options:
-V, --version Print version info and exit -V, --version Print version info and exit
--explain LINT Print the documentation for a given lint --explain LINT Print the documentation for a given lint
Other options are the same as `cargo check`. For the other options see `cargo check --help`.
To allow or deny a lint from the command line you can use `cargo clippy --` To allow or deny a lint from the command line you can use `cargo clippy --`
with: with:

View file

@ -7,6 +7,7 @@
#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)] #![warn(rust_2018_idioms, unused_lifetimes)]
use itertools::Itertools;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use test_utils::IS_RUSTC_TEST_SUITE; use test_utils::IS_RUSTC_TEST_SUITE;
@ -19,8 +20,10 @@ fn dogfood_clippy() {
return; return;
} }
let mut failed_packages = Vec::new();
// "" is the root package // "" is the root package
for package in &[ for package in [
"", "",
"clippy_dev", "clippy_dev",
"clippy_lints", "clippy_lints",
@ -28,8 +31,16 @@ fn dogfood_clippy() {
"lintcheck", "lintcheck",
"rustc_tools_util", "rustc_tools_util",
] { ] {
run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]); if !run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]) {
failed_packages.push(if package.is_empty() { "root" } else { package });
}
} }
assert!(
!failed_packages.is_empty(),
"Dogfood failed for packages `{}`",
failed_packages.iter().format(", "),
)
} }
#[test] #[test]
@ -71,7 +82,7 @@ fn run_metadata_collection_lint() {
run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]); run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
} }
fn run_clippy_for_package(project: &str, args: &[&str]) { fn run_clippy_for_package(project: &str, args: &[&str]) -> bool {
let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH);
@ -107,5 +118,5 @@ fn run_clippy_for_package(project: &str, args: &[&str]) {
println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
assert!(output.status.success()); output.status.success()
} }

View file

@ -45,24 +45,32 @@ impl_arith!(
Div, Custom, Custom, div; Div, Custom, Custom, div;
Mul, Custom, Custom, mul; Mul, Custom, Custom, mul;
Rem, Custom, Custom, rem; Rem, Custom, Custom, rem;
Shl, Custom, Custom, shl;
Shr, Custom, Custom, shr;
Sub, Custom, Custom, sub; Sub, Custom, Custom, sub;
Add, Custom, &Custom, add; Add, Custom, &Custom, add;
Div, Custom, &Custom, div; Div, Custom, &Custom, div;
Mul, Custom, &Custom, mul; Mul, Custom, &Custom, mul;
Rem, Custom, &Custom, rem; Rem, Custom, &Custom, rem;
Shl, Custom, &Custom, shl;
Shr, Custom, &Custom, shr;
Sub, Custom, &Custom, sub; Sub, Custom, &Custom, sub;
Add, &Custom, Custom, add; Add, &Custom, Custom, add;
Div, &Custom, Custom, div; Div, &Custom, Custom, div;
Mul, &Custom, Custom, mul; Mul, &Custom, Custom, mul;
Rem, &Custom, Custom, rem; Rem, &Custom, Custom, rem;
Shl, &Custom, Custom, shl;
Shr, &Custom, Custom, shr;
Sub, &Custom, Custom, sub; Sub, &Custom, Custom, sub;
Add, &Custom, &Custom, add; Add, &Custom, &Custom, add;
Div, &Custom, &Custom, div; Div, &Custom, &Custom, div;
Mul, &Custom, &Custom, mul; Mul, &Custom, &Custom, mul;
Rem, &Custom, &Custom, rem; Rem, &Custom, &Custom, rem;
Shl, &Custom, &Custom, shl;
Shr, &Custom, &Custom, shr;
Sub, &Custom, &Custom, sub; Sub, &Custom, &Custom, sub;
); );
@ -71,24 +79,32 @@ impl_assign_arith!(
DivAssign, Custom, Custom, div_assign; DivAssign, Custom, Custom, div_assign;
MulAssign, Custom, Custom, mul_assign; MulAssign, Custom, Custom, mul_assign;
RemAssign, Custom, Custom, rem_assign; RemAssign, Custom, Custom, rem_assign;
ShlAssign, Custom, Custom, shl_assign;
ShrAssign, Custom, Custom, shr_assign;
SubAssign, Custom, Custom, sub_assign; SubAssign, Custom, Custom, sub_assign;
AddAssign, Custom, &Custom, add_assign; AddAssign, Custom, &Custom, add_assign;
DivAssign, Custom, &Custom, div_assign; DivAssign, Custom, &Custom, div_assign;
MulAssign, Custom, &Custom, mul_assign; MulAssign, Custom, &Custom, mul_assign;
RemAssign, Custom, &Custom, rem_assign; RemAssign, Custom, &Custom, rem_assign;
ShlAssign, Custom, &Custom, shl_assign;
ShrAssign, Custom, &Custom, shr_assign;
SubAssign, Custom, &Custom, sub_assign; SubAssign, Custom, &Custom, sub_assign;
AddAssign, &Custom, Custom, add_assign; AddAssign, &Custom, Custom, add_assign;
DivAssign, &Custom, Custom, div_assign; DivAssign, &Custom, Custom, div_assign;
MulAssign, &Custom, Custom, mul_assign; MulAssign, &Custom, Custom, mul_assign;
RemAssign, &Custom, Custom, rem_assign; RemAssign, &Custom, Custom, rem_assign;
ShlAssign, &Custom, Custom, shl_assign;
ShrAssign, &Custom, Custom, shr_assign;
SubAssign, &Custom, Custom, sub_assign; SubAssign, &Custom, Custom, sub_assign;
AddAssign, &Custom, &Custom, add_assign; AddAssign, &Custom, &Custom, add_assign;
DivAssign, &Custom, &Custom, div_assign; DivAssign, &Custom, &Custom, div_assign;
MulAssign, &Custom, &Custom, mul_assign; MulAssign, &Custom, &Custom, mul_assign;
RemAssign, &Custom, &Custom, rem_assign; RemAssign, &Custom, &Custom, rem_assign;
ShlAssign, &Custom, &Custom, shl_assign;
ShrAssign, &Custom, &Custom, shr_assign;
SubAssign, &Custom, &Custom, sub_assign; SubAssign, &Custom, &Custom, sub_assign;
); );
@ -297,6 +313,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
_custom %= &Custom; _custom %= &Custom;
_custom *= Custom; _custom *= Custom;
_custom *= &Custom; _custom *= &Custom;
_custom >>= Custom;
_custom >>= &Custom;
_custom <<= Custom;
_custom <<= &Custom;
_custom += -Custom; _custom += -Custom;
_custom += &-Custom; _custom += &-Custom;
_custom -= -Custom; _custom -= -Custom;
@ -307,6 +327,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
_custom %= &-Custom; _custom %= &-Custom;
_custom *= -Custom; _custom *= -Custom;
_custom *= &-Custom; _custom *= &-Custom;
_custom >>= -Custom;
_custom >>= &-Custom;
_custom <<= -Custom;
_custom <<= &-Custom;
// Binary // Binary
_n = _n + 1; _n = _n + 1;
@ -347,6 +371,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
_custom = Custom + &Custom; _custom = Custom + &Custom;
_custom = &Custom + Custom; _custom = &Custom + Custom;
_custom = &Custom + &Custom; _custom = &Custom + &Custom;
_custom = _custom >> _custom;
_custom = _custom >> &_custom;
_custom = Custom << _custom;
_custom = &Custom << _custom;
// Unary // Unary
_n = -_n; _n = -_n;

View file

@ -1,5 +1,5 @@
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:270:5 --> $DIR/arithmetic_side_effects.rs:286:5
| |
LL | _n += 1; LL | _n += 1;
| ^^^^^^^ | ^^^^^^^
@ -7,592 +7,640 @@ LL | _n += 1;
= note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:271:5 --> $DIR/arithmetic_side_effects.rs:287:5
| |
LL | _n += &1; LL | _n += &1;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:272:5 --> $DIR/arithmetic_side_effects.rs:288:5
| |
LL | _n -= 1; LL | _n -= 1;
| ^^^^^^^ | ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:273:5 --> $DIR/arithmetic_side_effects.rs:289:5
| |
LL | _n -= &1; LL | _n -= &1;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:274:5 --> $DIR/arithmetic_side_effects.rs:290:5
| |
LL | _n /= 0; LL | _n /= 0;
| ^^^^^^^ | ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:275:5 --> $DIR/arithmetic_side_effects.rs:291:5
| |
LL | _n /= &0; LL | _n /= &0;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:276:5 --> $DIR/arithmetic_side_effects.rs:292:5
| |
LL | _n %= 0; LL | _n %= 0;
| ^^^^^^^ | ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:277:5 --> $DIR/arithmetic_side_effects.rs:293:5
| |
LL | _n %= &0; LL | _n %= &0;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:278:5 --> $DIR/arithmetic_side_effects.rs:294:5
| |
LL | _n *= 2; LL | _n *= 2;
| ^^^^^^^ | ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:279:5 --> $DIR/arithmetic_side_effects.rs:295:5
| |
LL | _n *= &2; LL | _n *= &2;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:280:5 --> $DIR/arithmetic_side_effects.rs:296:5
| |
LL | _n += -1; LL | _n += -1;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:281:5 --> $DIR/arithmetic_side_effects.rs:297:5
| |
LL | _n += &-1; LL | _n += &-1;
| ^^^^^^^^^ | ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:282:5 --> $DIR/arithmetic_side_effects.rs:298:5
| |
LL | _n -= -1; LL | _n -= -1;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:283:5 --> $DIR/arithmetic_side_effects.rs:299:5
| |
LL | _n -= &-1; LL | _n -= &-1;
| ^^^^^^^^^ | ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:284:5 --> $DIR/arithmetic_side_effects.rs:300:5
| |
LL | _n /= -0; LL | _n /= -0;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:285:5 --> $DIR/arithmetic_side_effects.rs:301:5
| |
LL | _n /= &-0; LL | _n /= &-0;
| ^^^^^^^^^ | ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:286:5 --> $DIR/arithmetic_side_effects.rs:302:5
| |
LL | _n %= -0; LL | _n %= -0;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:287:5 --> $DIR/arithmetic_side_effects.rs:303:5
| |
LL | _n %= &-0; LL | _n %= &-0;
| ^^^^^^^^^ | ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:288:5 --> $DIR/arithmetic_side_effects.rs:304:5
| |
LL | _n *= -2; LL | _n *= -2;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:289:5 --> $DIR/arithmetic_side_effects.rs:305:5
| |
LL | _n *= &-2; LL | _n *= &-2;
| ^^^^^^^^^ | ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:290:5 --> $DIR/arithmetic_side_effects.rs:306:5
| |
LL | _custom += Custom; LL | _custom += Custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:291:5 --> $DIR/arithmetic_side_effects.rs:307:5
| |
LL | _custom += &Custom; LL | _custom += &Custom;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:292:5 --> $DIR/arithmetic_side_effects.rs:308:5
| |
LL | _custom -= Custom; LL | _custom -= Custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:293:5 --> $DIR/arithmetic_side_effects.rs:309:5
| |
LL | _custom -= &Custom; LL | _custom -= &Custom;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:294:5 --> $DIR/arithmetic_side_effects.rs:310:5
| |
LL | _custom /= Custom; LL | _custom /= Custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:295:5 --> $DIR/arithmetic_side_effects.rs:311:5
| |
LL | _custom /= &Custom; LL | _custom /= &Custom;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:296:5 --> $DIR/arithmetic_side_effects.rs:312:5
| |
LL | _custom %= Custom; LL | _custom %= Custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:297:5 --> $DIR/arithmetic_side_effects.rs:313:5
| |
LL | _custom %= &Custom; LL | _custom %= &Custom;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:298:5 --> $DIR/arithmetic_side_effects.rs:314:5
| |
LL | _custom *= Custom; LL | _custom *= Custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:299:5 --> $DIR/arithmetic_side_effects.rs:315:5
| |
LL | _custom *= &Custom; LL | _custom *= &Custom;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:300:5 --> $DIR/arithmetic_side_effects.rs:316:5
|
LL | _custom >>= Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:317:5
|
LL | _custom >>= &Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:318:5
|
LL | _custom <<= Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:319:5
|
LL | _custom <<= &Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:320:5
| |
LL | _custom += -Custom; LL | _custom += -Custom;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:301:5 --> $DIR/arithmetic_side_effects.rs:321:5
| |
LL | _custom += &-Custom; LL | _custom += &-Custom;
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:302:5 --> $DIR/arithmetic_side_effects.rs:322:5
| |
LL | _custom -= -Custom; LL | _custom -= -Custom;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:303:5 --> $DIR/arithmetic_side_effects.rs:323:5
| |
LL | _custom -= &-Custom; LL | _custom -= &-Custom;
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:304:5 --> $DIR/arithmetic_side_effects.rs:324:5
| |
LL | _custom /= -Custom; LL | _custom /= -Custom;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:305:5 --> $DIR/arithmetic_side_effects.rs:325:5
| |
LL | _custom /= &-Custom; LL | _custom /= &-Custom;
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:306:5 --> $DIR/arithmetic_side_effects.rs:326:5
| |
LL | _custom %= -Custom; LL | _custom %= -Custom;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:307:5 --> $DIR/arithmetic_side_effects.rs:327:5
| |
LL | _custom %= &-Custom; LL | _custom %= &-Custom;
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:308:5 --> $DIR/arithmetic_side_effects.rs:328:5
| |
LL | _custom *= -Custom; LL | _custom *= -Custom;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:309:5 --> $DIR/arithmetic_side_effects.rs:329:5
| |
LL | _custom *= &-Custom; LL | _custom *= &-Custom;
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:312:10 --> $DIR/arithmetic_side_effects.rs:330:5
|
LL | _custom >>= -Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:331:5
|
LL | _custom >>= &-Custom;
| ^^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:332:5
|
LL | _custom <<= -Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:333:5
|
LL | _custom <<= &-Custom;
| ^^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:336:10
| |
LL | _n = _n + 1; LL | _n = _n + 1;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:313:10 --> $DIR/arithmetic_side_effects.rs:337:10
| |
LL | _n = _n + &1; LL | _n = _n + &1;
| ^^^^^^^ | ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:314:10 --> $DIR/arithmetic_side_effects.rs:338:10
| |
LL | _n = 1 + _n; LL | _n = 1 + _n;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:315:10 --> $DIR/arithmetic_side_effects.rs:339:10
| |
LL | _n = &1 + _n; LL | _n = &1 + _n;
| ^^^^^^^ | ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:316:10 --> $DIR/arithmetic_side_effects.rs:340:10
| |
LL | _n = _n - 1; LL | _n = _n - 1;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:317:10 --> $DIR/arithmetic_side_effects.rs:341:10
| |
LL | _n = _n - &1; LL | _n = _n - &1;
| ^^^^^^^ | ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:318:10 --> $DIR/arithmetic_side_effects.rs:342:10
| |
LL | _n = 1 - _n; LL | _n = 1 - _n;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:319:10 --> $DIR/arithmetic_side_effects.rs:343:10
| |
LL | _n = &1 - _n; LL | _n = &1 - _n;
| ^^^^^^^ | ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:320:10 --> $DIR/arithmetic_side_effects.rs:344:10
| |
LL | _n = _n / 0; LL | _n = _n / 0;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:321:10 --> $DIR/arithmetic_side_effects.rs:345:10
| |
LL | _n = _n / &0; LL | _n = _n / &0;
| ^^^^^^^ | ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:322:10 --> $DIR/arithmetic_side_effects.rs:346:10
| |
LL | _n = _n % 0; LL | _n = _n % 0;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:323:10 --> $DIR/arithmetic_side_effects.rs:347:10
| |
LL | _n = _n % &0; LL | _n = _n % &0;
| ^^^^^^^ | ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:324:10 --> $DIR/arithmetic_side_effects.rs:348:10
| |
LL | _n = _n * 2; LL | _n = _n * 2;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:325:10 --> $DIR/arithmetic_side_effects.rs:349:10
| |
LL | _n = _n * &2; LL | _n = _n * &2;
| ^^^^^^^ | ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:326:10 --> $DIR/arithmetic_side_effects.rs:350:10
| |
LL | _n = 2 * _n; LL | _n = 2 * _n;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:327:10 --> $DIR/arithmetic_side_effects.rs:351:10
| |
LL | _n = &2 * _n; LL | _n = &2 * _n;
| ^^^^^^^ | ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:328:10 --> $DIR/arithmetic_side_effects.rs:352:10
| |
LL | _n = 23 + &85; LL | _n = 23 + &85;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:329:10 --> $DIR/arithmetic_side_effects.rs:353:10
| |
LL | _n = &23 + 85; LL | _n = &23 + 85;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:330:10 --> $DIR/arithmetic_side_effects.rs:354:10
| |
LL | _n = &23 + &85; LL | _n = &23 + &85;
| ^^^^^^^^^ | ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:331:15 --> $DIR/arithmetic_side_effects.rs:355:15
| |
LL | _custom = _custom + _custom; LL | _custom = _custom + _custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:332:15 --> $DIR/arithmetic_side_effects.rs:356:15
| |
LL | _custom = _custom + &_custom; LL | _custom = _custom + &_custom;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:333:15 --> $DIR/arithmetic_side_effects.rs:357:15
| |
LL | _custom = Custom + _custom; LL | _custom = Custom + _custom;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:334:15 --> $DIR/arithmetic_side_effects.rs:358:15
| |
LL | _custom = &Custom + _custom; LL | _custom = &Custom + _custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:335:15 --> $DIR/arithmetic_side_effects.rs:359:15
| |
LL | _custom = _custom - Custom; LL | _custom = _custom - Custom;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:336:15 --> $DIR/arithmetic_side_effects.rs:360:15
| |
LL | _custom = _custom - &Custom; LL | _custom = _custom - &Custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:337:15 --> $DIR/arithmetic_side_effects.rs:361:15
| |
LL | _custom = Custom - _custom; LL | _custom = Custom - _custom;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:338:15 --> $DIR/arithmetic_side_effects.rs:362:15
| |
LL | _custom = &Custom - _custom; LL | _custom = &Custom - _custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:339:15 --> $DIR/arithmetic_side_effects.rs:363:15
| |
LL | _custom = _custom / Custom; LL | _custom = _custom / Custom;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:340:15 --> $DIR/arithmetic_side_effects.rs:364:15
| |
LL | _custom = _custom / &Custom; LL | _custom = _custom / &Custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:341:15 --> $DIR/arithmetic_side_effects.rs:365:15
| |
LL | _custom = _custom % Custom; LL | _custom = _custom % Custom;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:342:15 --> $DIR/arithmetic_side_effects.rs:366:15
| |
LL | _custom = _custom % &Custom; LL | _custom = _custom % &Custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:343:15 --> $DIR/arithmetic_side_effects.rs:367:15
| |
LL | _custom = _custom * Custom; LL | _custom = _custom * Custom;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:344:15 --> $DIR/arithmetic_side_effects.rs:368:15
| |
LL | _custom = _custom * &Custom; LL | _custom = _custom * &Custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:345:15 --> $DIR/arithmetic_side_effects.rs:369:15
| |
LL | _custom = Custom * _custom; LL | _custom = Custom * _custom;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:346:15 --> $DIR/arithmetic_side_effects.rs:370:15
| |
LL | _custom = &Custom * _custom; LL | _custom = &Custom * _custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:347:15 --> $DIR/arithmetic_side_effects.rs:371:15
| |
LL | _custom = Custom + &Custom; LL | _custom = Custom + &Custom;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:348:15 --> $DIR/arithmetic_side_effects.rs:372:15
| |
LL | _custom = &Custom + Custom; LL | _custom = &Custom + Custom;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:349:15 --> $DIR/arithmetic_side_effects.rs:373:15
| |
LL | _custom = &Custom + &Custom; LL | _custom = &Custom + &Custom;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:352:10 --> $DIR/arithmetic_side_effects.rs:374:15
|
LL | _custom = _custom >> _custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:375:15
|
LL | _custom = _custom >> &_custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:376:15
|
LL | _custom = Custom << _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:377:15
|
LL | _custom = &Custom << _custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:380:10
| |
LL | _n = -_n; LL | _n = -_n;
| ^^^ | ^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:353:10 --> $DIR/arithmetic_side_effects.rs:381:10
| |
LL | _n = -&_n; LL | _n = -&_n;
| ^^^^ | ^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:354:15 --> $DIR/arithmetic_side_effects.rs:382:15
| |
LL | _custom = -_custom; LL | _custom = -_custom;
| ^^^^^^^^ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:355:15 --> $DIR/arithmetic_side_effects.rs:383:15
| |
LL | _custom = -&_custom; LL | _custom = -&_custom;
| ^^^^^^^^^ | ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:364:5 --> $DIR/arithmetic_side_effects.rs:392:5
| |
LL | 1 + i; LL | 1 + i;
| ^^^^^ | ^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:365:5 --> $DIR/arithmetic_side_effects.rs:393:5
| |
LL | i * 2; LL | i * 2;
| ^^^^^ | ^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:367:5 --> $DIR/arithmetic_side_effects.rs:395:5
| |
LL | i - 2 + 2 - i; LL | i - 2 + 2 - i;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:368:5 --> $DIR/arithmetic_side_effects.rs:396:5
| |
LL | -i; LL | -i;
| ^^ | ^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:369:5 --> $DIR/arithmetic_side_effects.rs:407:5
|
LL | i >> 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:370:5
|
LL | i << 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:379:5
| |
LL | i += 1; LL | i += 1;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:380:5 --> $DIR/arithmetic_side_effects.rs:408:5
| |
LL | i -= 1; LL | i -= 1;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:381:5 --> $DIR/arithmetic_side_effects.rs:409:5
| |
LL | i *= 2; LL | i *= 2;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:383:5 --> $DIR/arithmetic_side_effects.rs:411:5
| |
LL | i /= 0; LL | i /= 0;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:385:5 --> $DIR/arithmetic_side_effects.rs:413:5
| |
LL | i /= var1; LL | i /= var1;
| ^^^^^^^^^ | ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:386:5 --> $DIR/arithmetic_side_effects.rs:414:5
| |
LL | i /= var2; LL | i /= var2;
| ^^^^^^^^^ | ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:388:5 --> $DIR/arithmetic_side_effects.rs:416:5
| |
LL | i %= 0; LL | i %= 0;
| ^^^^^^ | ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:390:5 --> $DIR/arithmetic_side_effects.rs:418:5
| |
LL | i %= var1; LL | i %= var1;
| ^^^^^^^^^ | ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:391:5 --> $DIR/arithmetic_side_effects.rs:419:5
| |
LL | i %= var2; LL | i %= var2;
| ^^^^^^^^^ | ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: aborting due to 107 previous errors
--> $DIR/arithmetic_side_effects.rs:392:5
|
LL | i <<= 3;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:393:5
|
LL | i >>= 2;
| ^^^^^^^
error: aborting due to 99 previous errors

View file

@ -2,6 +2,7 @@
#![feature(lint_reasons)] #![feature(lint_reasons)]
#![feature(async_closure)] #![feature(async_closure)]
#![warn(clippy::async_yields_async)] #![warn(clippy::async_yields_async)]
#![allow(clippy::redundant_async_block)]
use core::future::Future; use core::future::Future;
use core::pin::Pin; use core::pin::Pin;

View file

@ -2,6 +2,7 @@
#![feature(lint_reasons)] #![feature(lint_reasons)]
#![feature(async_closure)] #![feature(async_closure)]
#![warn(clippy::async_yields_async)] #![warn(clippy::async_yields_async)]
#![allow(clippy::redundant_async_block)]
use core::future::Future; use core::future::Future;
use core::pin::Pin; use core::pin::Pin;

View file

@ -1,5 +1,5 @@
error: an async construct yields a type which is itself awaitable error: an async construct yields a type which is itself awaitable
--> $DIR/async_yields_async.rs:39:9 --> $DIR/async_yields_async.rs:40:9
| |
LL | let _h = async { LL | let _h = async {
| _____________________- | _____________________-
@ -19,7 +19,7 @@ LL + }.await
| |
error: an async construct yields a type which is itself awaitable error: an async construct yields a type which is itself awaitable
--> $DIR/async_yields_async.rs:44:9 --> $DIR/async_yields_async.rs:45:9
| |
LL | let _i = async { LL | let _i = async {
| ____________________- | ____________________-
@ -32,7 +32,7 @@ LL | | };
| |_____- outer async construct | |_____- outer async construct
error: an async construct yields a type which is itself awaitable error: an async construct yields a type which is itself awaitable
--> $DIR/async_yields_async.rs:50:9 --> $DIR/async_yields_async.rs:51:9
| |
LL | let _j = async || { LL | let _j = async || {
| ________________________- | ________________________-
@ -51,7 +51,7 @@ LL + }.await
| |
error: an async construct yields a type which is itself awaitable error: an async construct yields a type which is itself awaitable
--> $DIR/async_yields_async.rs:55:9 --> $DIR/async_yields_async.rs:56:9
| |
LL | let _k = async || { LL | let _k = async || {
| _______________________- | _______________________-
@ -64,7 +64,7 @@ LL | | };
| |_____- outer async construct | |_____- outer async construct
error: an async construct yields a type which is itself awaitable error: an async construct yields a type which is itself awaitable
--> $DIR/async_yields_async.rs:57:23 --> $DIR/async_yields_async.rs:58:23
| |
LL | let _l = async || CustomFutureType; LL | let _l = async || CustomFutureType;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
@ -74,7 +74,7 @@ LL | let _l = async || CustomFutureType;
| help: consider awaiting this value: `CustomFutureType.await` | help: consider awaiting this value: `CustomFutureType.await`
error: an async construct yields a type which is itself awaitable error: an async construct yields a type which is itself awaitable
--> $DIR/async_yields_async.rs:63:9 --> $DIR/async_yields_async.rs:64:9
| |
LL | let _m = async || { LL | let _m = async || {
| _______________________- | _______________________-

View file

@ -0,0 +1,165 @@
#![allow(unused)]
#![warn(clippy::collection_is_never_read)]
use std::collections::{HashMap, HashSet};
fn main() {}
fn not_a_collection() {
// TODO: Expand `collection_is_never_read` beyond collections?
let mut x = 10; // Ok
x += 1;
}
fn no_access_at_all() {
// Other lints should catch this.
let x = vec![1, 2, 3]; // Ok
}
fn write_without_read() {
// The main use case for `collection_is_never_read`.
let mut x = HashMap::new(); // WARNING
x.insert(1, 2);
}
fn read_without_write() {
let mut x = vec![1, 2, 3]; // Ok
let _ = x.len();
}
fn write_and_read() {
let mut x = vec![1, 2, 3]; // Ok
x.push(4);
let _ = x.len();
}
fn write_after_read() {
// TODO: Warn here, but this requires more extensive data flow analysis.
let mut x = vec![1, 2, 3]; // Ok
let _ = x.len();
x.push(4); // Pointless
}
fn write_before_reassign() {
// TODO: Warn here, but this requires more extensive data flow analysis.
let mut x = HashMap::new(); // Ok
x.insert(1, 2); // Pointless
x = HashMap::new();
let _ = x.len();
}
fn read_in_closure() {
let mut x = HashMap::new(); // Ok
x.insert(1, 2);
let _ = || {
let _ = x.len();
};
}
fn write_in_closure() {
let mut x = vec![1, 2, 3]; // WARNING
let _ = || {
x.push(4);
};
}
fn read_in_format() {
let mut x = HashMap::new(); // Ok
x.insert(1, 2);
format!("{x:?}");
}
fn shadowing_1() {
let x = HashMap::<usize, usize>::new(); // Ok
let _ = x.len();
let mut x = HashMap::new(); // WARNING
x.insert(1, 2);
}
fn shadowing_2() {
let mut x = HashMap::new(); // WARNING
x.insert(1, 2);
let x = HashMap::<usize, usize>::new(); // Ok
let _ = x.len();
}
#[allow(clippy::let_unit_value)]
fn fake_read() {
let mut x = vec![1, 2, 3]; // Ok
x.reverse();
// `collection_is_never_read` gets fooled, but other lints should catch this.
let _: () = x.clear();
}
fn assignment() {
let mut x = vec![1, 2, 3]; // WARNING
let y = vec![4, 5, 6]; // Ok
x = y;
}
#[allow(clippy::self_assignment)]
fn self_assignment() {
let mut x = vec![1, 2, 3]; // WARNING
x = x;
}
fn method_argument_but_not_target() {
struct MyStruct;
impl MyStruct {
fn my_method(&self, _argument: &[usize]) {}
}
let my_struct = MyStruct;
let mut x = vec![1, 2, 3]; // Ok
x.reverse();
my_struct.my_method(&x);
}
fn insert_is_not_a_read() {
let mut x = HashSet::new(); // WARNING
x.insert(5);
}
fn insert_is_a_read() {
let mut x = HashSet::new(); // Ok
if x.insert(5) {
println!("5 was inserted");
}
}
fn not_read_if_return_value_not_used() {
// `is_empty` does not modify the set, so it's a query. But since the return value is not used, the
// lint does not consider it a read here.
let x = vec![1, 2, 3]; // WARNING
x.is_empty();
}
fn extension_traits() {
trait VecExt<T> {
fn method_with_side_effect(&self);
fn method_without_side_effect(&self);
}
impl<T> VecExt<T> for Vec<T> {
fn method_with_side_effect(&self) {
println!("my length: {}", self.len());
}
fn method_without_side_effect(&self) {}
}
let x = vec![1, 2, 3]; // Ok
x.method_with_side_effect();
let y = vec![1, 2, 3]; // Ok (false negative)
y.method_without_side_effect();
}
fn function_argument() {
#[allow(clippy::ptr_arg)]
fn foo<T>(v: &Vec<T>) -> usize {
v.len()
}
let x = vec![1, 2, 3]; // Ok
foo(&x);
}

View file

@ -0,0 +1,52 @@
error: collection is never read
--> $DIR/collection_is_never_read.rs:21:5
|
LL | let mut x = HashMap::new(); // WARNING
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::collection-is-never-read` implied by `-D warnings`
error: collection is never read
--> $DIR/collection_is_never_read.rs:60:5
|
LL | let mut x = vec![1, 2, 3]; // WARNING
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: collection is never read
--> $DIR/collection_is_never_read.rs:75:5
|
LL | let mut x = HashMap::new(); // WARNING
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: collection is never read
--> $DIR/collection_is_never_read.rs:80:5
|
LL | let mut x = HashMap::new(); // WARNING
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: collection is never read
--> $DIR/collection_is_never_read.rs:95:5
|
LL | let mut x = vec![1, 2, 3]; // WARNING
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: collection is never read
--> $DIR/collection_is_never_read.rs:102:5
|
LL | let mut x = vec![1, 2, 3]; // WARNING
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: collection is never read
--> $DIR/collection_is_never_read.rs:119:5
|
LL | let mut x = HashSet::new(); // WARNING
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: collection is never read
--> $DIR/collection_is_never_read.rs:133:5
|
LL | let x = vec![1, 2, 3]; // WARNING
| ^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 8 previous errors

View file

@ -0,0 +1,9 @@
// aux-build:../../auxiliary/proc_macro_with_span.rs
extern crate proc_macro_with_span;
use proc_macro_with_span::with_span;
fn main() {
println!(with_span!(""something ""));
}

View file

@ -0,0 +1,12 @@
error: empty string literal in `println!`
--> $DIR/ice-10148.rs:8:5
|
LL | println!(with_span!(""something ""));
| ^^^^^^^^^^^^^^^^^^^^-----------^^^^^
| |
| help: remove the empty string
|
= note: `-D clippy::println-empty-string` implied by `-D warnings`
error: aborting due to previous error

View file

@ -2,7 +2,7 @@
//! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details. //! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details.
#![warn(clippy::use_self)] #![warn(clippy::use_self)]
#![allow(dead_code)] #![allow(dead_code, clippy::let_with_type_underscore)]
struct Foo; struct Foo;

View file

@ -0,0 +1,17 @@
#![allow(dead_code)]
struct Foo;
impl<'a> std::convert::TryFrom<&'a String> for Foo {
type Error = std::convert::Infallible;
fn try_from(_: &'a String) -> Result<Self, Self::Error> {
Ok(Foo)
}
}
fn find<E>(_: impl std::convert::TryInto<Foo, Error = E>) {}
fn main() {
find(&String::new());
}

View file

@ -9,7 +9,8 @@
clippy::unnecessary_operation, clippy::unnecessary_operation,
clippy::branches_sharing_code, clippy::branches_sharing_code,
clippy::match_single_binding, clippy::match_single_binding,
clippy::let_unit_value clippy::let_unit_value,
clippy::let_with_type_underscore
)] )]
#[macro_use] #[macro_use]

View file

@ -9,7 +9,8 @@
clippy::unnecessary_operation, clippy::unnecessary_operation,
clippy::branches_sharing_code, clippy::branches_sharing_code,
clippy::match_single_binding, clippy::match_single_binding,
clippy::let_unit_value clippy::let_unit_value,
clippy::let_with_type_underscore
)] )]
#[macro_use] #[macro_use]

View file

@ -1,5 +1,5 @@
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:21:17 --> $DIR/default_numeric_fallback_f64.rs:22:17
| |
LL | let x = 0.12; LL | let x = 0.12;
| ^^^^ help: consider adding suffix: `0.12_f64` | ^^^^ help: consider adding suffix: `0.12_f64`
@ -7,139 +7,139 @@ LL | let x = 0.12;
= note: `-D clippy::default-numeric-fallback` implied by `-D warnings` = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:22:18 --> $DIR/default_numeric_fallback_f64.rs:23:18
| |
LL | let x = [1., 2., 3.]; LL | let x = [1., 2., 3.];
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:22:22 --> $DIR/default_numeric_fallback_f64.rs:23:22
| |
LL | let x = [1., 2., 3.]; LL | let x = [1., 2., 3.];
| ^^ help: consider adding suffix: `2.0_f64` | ^^ help: consider adding suffix: `2.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:22:26 --> $DIR/default_numeric_fallback_f64.rs:23:26
| |
LL | let x = [1., 2., 3.]; LL | let x = [1., 2., 3.];
| ^^ help: consider adding suffix: `3.0_f64` | ^^ help: consider adding suffix: `3.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:23:28 --> $DIR/default_numeric_fallback_f64.rs:24:28
| |
LL | let x = if true { (1., 2.) } else { (3., 4.) }; LL | let x = if true { (1., 2.) } else { (3., 4.) };
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:23:32 --> $DIR/default_numeric_fallback_f64.rs:24:32
| |
LL | let x = if true { (1., 2.) } else { (3., 4.) }; LL | let x = if true { (1., 2.) } else { (3., 4.) };
| ^^ help: consider adding suffix: `2.0_f64` | ^^ help: consider adding suffix: `2.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:23:46 --> $DIR/default_numeric_fallback_f64.rs:24:46
| |
LL | let x = if true { (1., 2.) } else { (3., 4.) }; LL | let x = if true { (1., 2.) } else { (3., 4.) };
| ^^ help: consider adding suffix: `3.0_f64` | ^^ help: consider adding suffix: `3.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:23:50 --> $DIR/default_numeric_fallback_f64.rs:24:50
| |
LL | let x = if true { (1., 2.) } else { (3., 4.) }; LL | let x = if true { (1., 2.) } else { (3., 4.) };
| ^^ help: consider adding suffix: `4.0_f64` | ^^ help: consider adding suffix: `4.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:24:23 --> $DIR/default_numeric_fallback_f64.rs:25:23
| |
LL | let x = match 1. { LL | let x = match 1. {
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:25:18 --> $DIR/default_numeric_fallback_f64.rs:26:18
| |
LL | _ => 1., LL | _ => 1.,
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:44:21 --> $DIR/default_numeric_fallback_f64.rs:45:21
| |
LL | let y = 1.; LL | let y = 1.;
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:52:21 --> $DIR/default_numeric_fallback_f64.rs:53:21
| |
LL | let y = 1.; LL | let y = 1.;
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:58:21 --> $DIR/default_numeric_fallback_f64.rs:59:21
| |
LL | let y = 1.; LL | let y = 1.;
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:66:21 --> $DIR/default_numeric_fallback_f64.rs:67:21
| |
LL | let y = 1.; LL | let y = 1.;
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:78:9 --> $DIR/default_numeric_fallback_f64.rs:79:9
| |
LL | 1. LL | 1.
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:84:27 --> $DIR/default_numeric_fallback_f64.rs:85:27
| |
LL | let f = || -> _ { 1. }; LL | let f = || -> _ { 1. };
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:88:29 --> $DIR/default_numeric_fallback_f64.rs:89:29
| |
LL | let f = || -> f64 { 1. }; LL | let f = || -> f64 { 1. };
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:102:21 --> $DIR/default_numeric_fallback_f64.rs:103:21
| |
LL | generic_arg(1.); LL | generic_arg(1.);
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:105:32 --> $DIR/default_numeric_fallback_f64.rs:106:32
| |
LL | let x: _ = generic_arg(1.); LL | let x: _ = generic_arg(1.);
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:123:28 --> $DIR/default_numeric_fallback_f64.rs:124:28
| |
LL | GenericStruct { x: 1. }; LL | GenericStruct { x: 1. };
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:126:36 --> $DIR/default_numeric_fallback_f64.rs:127:36
| |
LL | let _ = GenericStruct { x: 1. }; LL | let _ = GenericStruct { x: 1. };
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:144:24 --> $DIR/default_numeric_fallback_f64.rs:145:24
| |
LL | GenericEnum::X(1.); LL | GenericEnum::X(1.);
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:164:23 --> $DIR/default_numeric_fallback_f64.rs:165:23
| |
LL | s.generic_arg(1.); LL | s.generic_arg(1.);
| ^^ help: consider adding suffix: `1.0_f64` | ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:171:21 --> $DIR/default_numeric_fallback_f64.rs:172:21
| |
LL | let x = 22.; LL | let x = 22.;
| ^^^ help: consider adding suffix: `22.0_f64` | ^^^ help: consider adding suffix: `22.0_f64`

View file

@ -9,7 +9,8 @@
clippy::no_effect, clippy::no_effect,
clippy::unnecessary_operation, clippy::unnecessary_operation,
clippy::branches_sharing_code, clippy::branches_sharing_code,
clippy::let_unit_value clippy::let_unit_value,
clippy::let_with_type_underscore
)] )]
#[macro_use] #[macro_use]

View file

@ -9,7 +9,8 @@
clippy::no_effect, clippy::no_effect,
clippy::unnecessary_operation, clippy::unnecessary_operation,
clippy::branches_sharing_code, clippy::branches_sharing_code,
clippy::let_unit_value clippy::let_unit_value,
clippy::let_with_type_underscore
)] )]
#[macro_use] #[macro_use]

View file

@ -1,5 +1,5 @@
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:21:17 --> $DIR/default_numeric_fallback_i32.rs:22:17
| |
LL | let x = 22; LL | let x = 22;
| ^^ help: consider adding suffix: `22_i32` | ^^ help: consider adding suffix: `22_i32`
@ -7,151 +7,151 @@ LL | let x = 22;
= note: `-D clippy::default-numeric-fallback` implied by `-D warnings` = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:22:18 --> $DIR/default_numeric_fallback_i32.rs:23:18
| |
LL | let x = [1, 2, 3]; LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:22:21 --> $DIR/default_numeric_fallback_i32.rs:23:21
| |
LL | let x = [1, 2, 3]; LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `2_i32` | ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:22:24 --> $DIR/default_numeric_fallback_i32.rs:23:24
| |
LL | let x = [1, 2, 3]; LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `3_i32` | ^ help: consider adding suffix: `3_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:23:28 --> $DIR/default_numeric_fallback_i32.rs:24:28
| |
LL | let x = if true { (1, 2) } else { (3, 4) }; LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:23:31 --> $DIR/default_numeric_fallback_i32.rs:24:31
| |
LL | let x = if true { (1, 2) } else { (3, 4) }; LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `2_i32` | ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:23:44 --> $DIR/default_numeric_fallback_i32.rs:24:44
| |
LL | let x = if true { (1, 2) } else { (3, 4) }; LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `3_i32` | ^ help: consider adding suffix: `3_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:23:47 --> $DIR/default_numeric_fallback_i32.rs:24:47
| |
LL | let x = if true { (1, 2) } else { (3, 4) }; LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `4_i32` | ^ help: consider adding suffix: `4_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:24:23 --> $DIR/default_numeric_fallback_i32.rs:25:23
| |
LL | let x = match 1 { LL | let x = match 1 {
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:25:13 --> $DIR/default_numeric_fallback_i32.rs:26:13
| |
LL | 1 => 1, LL | 1 => 1,
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:25:18 --> $DIR/default_numeric_fallback_i32.rs:26:18
| |
LL | 1 => 1, LL | 1 => 1,
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:26:18 --> $DIR/default_numeric_fallback_i32.rs:27:18
| |
LL | _ => 2, LL | _ => 2,
| ^ help: consider adding suffix: `2_i32` | ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:45:21 --> $DIR/default_numeric_fallback_i32.rs:46:21
| |
LL | let y = 1; LL | let y = 1;
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:53:21 --> $DIR/default_numeric_fallback_i32.rs:54:21
| |
LL | let y = 1; LL | let y = 1;
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:59:21 --> $DIR/default_numeric_fallback_i32.rs:60:21
| |
LL | let y = 1; LL | let y = 1;
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:67:21 --> $DIR/default_numeric_fallback_i32.rs:68:21
| |
LL | let y = 1; LL | let y = 1;
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:79:9 --> $DIR/default_numeric_fallback_i32.rs:80:9
| |
LL | 1 LL | 1
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:85:27 --> $DIR/default_numeric_fallback_i32.rs:86:27
| |
LL | let f = || -> _ { 1 }; LL | let f = || -> _ { 1 };
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:89:29 --> $DIR/default_numeric_fallback_i32.rs:90:29
| |
LL | let f = || -> i32 { 1 }; LL | let f = || -> i32 { 1 };
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:103:21 --> $DIR/default_numeric_fallback_i32.rs:104:21
| |
LL | generic_arg(1); LL | generic_arg(1);
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:106:32 --> $DIR/default_numeric_fallback_i32.rs:107:32
| |
LL | let x: _ = generic_arg(1); LL | let x: _ = generic_arg(1);
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:124:28 --> $DIR/default_numeric_fallback_i32.rs:125:28
| |
LL | GenericStruct { x: 1 }; LL | GenericStruct { x: 1 };
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:127:36 --> $DIR/default_numeric_fallback_i32.rs:128:36
| |
LL | let _ = GenericStruct { x: 1 }; LL | let _ = GenericStruct { x: 1 };
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:145:24 --> $DIR/default_numeric_fallback_i32.rs:146:24
| |
LL | GenericEnum::X(1); LL | GenericEnum::X(1);
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:165:23 --> $DIR/default_numeric_fallback_i32.rs:166:23
| |
LL | s.generic_arg(1); LL | s.generic_arg(1);
| ^ help: consider adding suffix: `1_i32` | ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:172:21 --> $DIR/default_numeric_fallback_i32.rs:173:21
| |
LL | let x = 22; LL | let x = 22;
| ^^ help: consider adding suffix: `22_i32` | ^^ help: consider adding suffix: `22_i32`

View file

@ -231,4 +231,41 @@ impl Default for NonExhaustiveEnum {
} }
} }
// https://github.com/rust-lang/rust-clippy/issues/10396
#[derive(Default)]
struct DefaultType;
struct GenericType<T = DefaultType> {
t: T,
}
impl Default for GenericType {
fn default() -> Self {
Self { t: Default::default() }
}
}
struct InnerGenericType<T> {
t: T,
}
impl Default for InnerGenericType<DefaultType> {
fn default() -> Self {
Self { t: Default::default() }
}
}
struct OtherGenericType<T = DefaultType> {
inner: InnerGenericType<T>,
}
impl Default for OtherGenericType {
fn default() -> Self {
Self {
inner: Default::default(),
}
}
}
fn main() {} fn main() {}

View file

@ -267,4 +267,41 @@ impl Default for NonExhaustiveEnum {
} }
} }
// https://github.com/rust-lang/rust-clippy/issues/10396
#[derive(Default)]
struct DefaultType;
struct GenericType<T = DefaultType> {
t: T,
}
impl Default for GenericType {
fn default() -> Self {
Self { t: Default::default() }
}
}
struct InnerGenericType<T> {
t: T,
}
impl Default for InnerGenericType<DefaultType> {
fn default() -> Self {
Self { t: Default::default() }
}
}
struct OtherGenericType<T = DefaultType> {
inner: InnerGenericType<T>,
}
impl Default for OtherGenericType {
fn default() -> Self {
Self {
inner: Default::default(),
}
}
}
fn main() {} fn main() {}

View file

@ -1,5 +1,4 @@
// run-rustfix // run-rustfix
// aux-build: proc_macro_with_span.rs
#![warn(clippy::useless_format)] #![warn(clippy::useless_format)]
#![allow( #![allow(
unused_tuple_struct_fields, unused_tuple_struct_fields,
@ -10,8 +9,6 @@
clippy::uninlined_format_args clippy::uninlined_format_args
)] )]
extern crate proc_macro_with_span;
struct Foo(pub String); struct Foo(pub String);
macro_rules! foo { macro_rules! foo {
@ -90,7 +87,4 @@ fn main() {
let _ = abc.to_string(); let _ = abc.to_string();
let xx = "xx"; let xx = "xx";
let _ = xx.to_string(); let _ = xx.to_string();
// Issue #10148
println!(proc_macro_with_span::with_span!(""something ""));
} }

View file

@ -1,5 +1,4 @@
// run-rustfix // run-rustfix
// aux-build: proc_macro_with_span.rs
#![warn(clippy::useless_format)] #![warn(clippy::useless_format)]
#![allow( #![allow(
unused_tuple_struct_fields, unused_tuple_struct_fields,
@ -10,8 +9,6 @@
clippy::uninlined_format_args clippy::uninlined_format_args
)] )]
extern crate proc_macro_with_span;
struct Foo(pub String); struct Foo(pub String);
macro_rules! foo { macro_rules! foo {
@ -92,7 +89,4 @@ fn main() {
let _ = format!("{abc}"); let _ = format!("{abc}");
let xx = "xx"; let xx = "xx";
let _ = format!("{xx}"); let _ = format!("{xx}");
// Issue #10148
println!(proc_macro_with_span::with_span!(""something ""));
} }

View file

@ -1,5 +1,5 @@
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:22:5 --> $DIR/format.rs:19:5
| |
LL | format!("foo"); LL | format!("foo");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
@ -7,19 +7,19 @@ LL | format!("foo");
= note: `-D clippy::useless-format` implied by `-D warnings` = note: `-D clippy::useless-format` implied by `-D warnings`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:23:5 --> $DIR/format.rs:20:5
| |
LL | format!("{{}}"); LL | format!("{{}}");
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()` | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:24:5 --> $DIR/format.rs:21:5
| |
LL | format!("{{}} abc {{}}"); LL | format!("{{}} abc {{}}");
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()` | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:25:5 --> $DIR/format.rs:22:5
| |
LL | / format!( LL | / format!(
LL | | r##"foo {{}} LL | | r##"foo {{}}
@ -34,67 +34,67 @@ LL ~ " bar"##.to_string();
| |
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:30:13 --> $DIR/format.rs:27:13
| |
LL | let _ = format!(""); LL | let _ = format!("");
| ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()` | ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:32:5 --> $DIR/format.rs:29:5
| |
LL | format!("{}", "foo"); LL | format!("{}", "foo");
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:40:5 --> $DIR/format.rs:37:5
| |
LL | format!("{}", arg); LL | format!("{}", arg);
| ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:70:5 --> $DIR/format.rs:67:5
| |
LL | format!("{}", 42.to_string()); LL | format!("{}", 42.to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:72:5 --> $DIR/format.rs:69:5
| |
LL | format!("{}", x.display().to_string()); LL | format!("{}", x.display().to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:76:18 --> $DIR/format.rs:73:18
| |
LL | let _ = Some(format!("{}", a + "bar")); LL | let _ = Some(format!("{}", a + "bar"));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:80:22 --> $DIR/format.rs:77:22
| |
LL | let _s: String = format!("{}", &*v.join("/n")); LL | let _s: String = format!("{}", &*v.join("/n"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:86:13 --> $DIR/format.rs:83:13
| |
LL | let _ = format!("{x}"); LL | let _ = format!("{x}");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:88:13 --> $DIR/format.rs:85:13
| |
LL | let _ = format!("{y}", y = x); LL | let _ = format!("{y}", y = x);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:92:13 --> $DIR/format.rs:89:13
| |
LL | let _ = format!("{abc}"); LL | let _ = format!("{abc}");
| ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()` | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
error: useless use of `format!` error: useless use of `format!`
--> $DIR/format.rs:94:13 --> $DIR/format.rs:91:13
| |
LL | let _ = format!("{xx}"); LL | let _ = format!("{xx}");
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()` | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`

View file

@ -5,7 +5,7 @@ LL | pub fn a(_: impl Trait) {}
| ^^^^^^^^^^ | ^^^^^^^^^^
| |
= note: `-D clippy::impl-trait-in-params` implied by `-D warnings` = note: `-D clippy::impl-trait-in-params` implied by `-D warnings`
help: add a type paremeter help: add a type parameter
| |
LL | pub fn a<{ /* Generic name */ }: Trait>(_: impl Trait) {} LL | pub fn a<{ /* Generic name */ }: Trait>(_: impl Trait) {}
| +++++++++++++++++++++++++++++++ | +++++++++++++++++++++++++++++++
@ -16,7 +16,7 @@ error: '`impl Trait` used as a function parameter'
LL | pub fn c<C: Trait>(_: C, _: impl Trait) {} LL | pub fn c<C: Trait>(_: C, _: impl Trait) {}
| ^^^^^^^^^^ | ^^^^^^^^^^
| |
help: add a type paremeter help: add a type parameter
| |
LL | pub fn c<C: Trait, { /* Generic name */ }: Trait>(_: C, _: impl Trait) {} LL | pub fn c<C: Trait, { /* Generic name */ }: Trait>(_: C, _: impl Trait) {}
| +++++++++++++++++++++++++++++++ | +++++++++++++++++++++++++++++++

View file

@ -87,7 +87,7 @@ fn main() {
let kitten = Kitten {}; let kitten = Kitten {};
let _ = kitten.clone(); let _ = kitten.clone();
let _ = own_same_from_ref(&kitten); let _ = own_same_from_ref(&kitten);
// this shouln't lint // this shouldn't lint
let _ = kitten.to_vec(); let _ = kitten.to_vec();
// we expect no lints for this // we expect no lints for this

View file

@ -87,7 +87,7 @@ fn main() {
let kitten = Kitten {}; let kitten = Kitten {};
let _ = kitten.to_owned(); let _ = kitten.to_owned();
let _ = own_same_from_ref(&kitten); let _ = own_same_from_ref(&kitten);
// this shouln't lint // this shouldn't lint
let _ = kitten.to_vec(); let _ = kitten.to_vec();
// we expect no lints for this // we expect no lints for this

View file

@ -282,6 +282,87 @@ impl AsyncLen {
} }
} }
// issue #7232
pub struct AsyncLenWithoutIsEmpty;
impl AsyncLenWithoutIsEmpty {
pub async fn async_task(&self) -> bool {
true
}
pub async fn len(&self) -> usize {
usize::from(!self.async_task().await)
}
}
// issue #7232
pub struct AsyncOptionLenWithoutIsEmpty;
impl AsyncOptionLenWithoutIsEmpty {
async fn async_task(&self) -> bool {
true
}
pub async fn len(&self) -> Option<usize> {
None
}
}
// issue #7232
pub struct AsyncOptionLenNonIntegral;
impl AsyncOptionLenNonIntegral {
// don't lint
pub async fn len(&self) -> Option<String> {
None
}
}
// issue #7232
pub struct AsyncResultLenWithoutIsEmpty;
impl AsyncResultLenWithoutIsEmpty {
async fn async_task(&self) -> bool {
true
}
pub async fn len(&self) -> Result<usize, ()> {
Err(())
}
}
// issue #7232
pub struct AsyncOptionLen;
impl AsyncOptionLen {
async fn async_task(&self) -> bool {
true
}
pub async fn len(&self) -> Result<usize, ()> {
Err(())
}
pub async fn is_empty(&self) -> bool {
true
}
}
pub struct AsyncLenSyncIsEmpty;
impl AsyncLenSyncIsEmpty {
pub async fn len(&self) -> u32 {
0
}
pub fn is_empty(&self) -> bool {
true
}
}
// issue #9520
pub struct NonStandardLen;
impl NonStandardLen {
// don't lint
pub fn len(&self, something: usize) -> usize {
something
}
}
// issue #9520 // issue #9520
pub struct NonStandardLenAndIsEmptySignature; pub struct NonStandardLenAndIsEmptySignature;
impl NonStandardLenAndIsEmptySignature { impl NonStandardLenAndIsEmptySignature {
@ -328,4 +409,15 @@ impl NonStandardSignatureWithGenerics {
} }
} }
pub struct DifferingErrors;
impl DifferingErrors {
pub fn len(&self) -> Result<usize, u8> {
Ok(0)
}
pub fn is_empty(&self) -> Result<bool, u16> {
Ok(true)
}
}
fn main() {} fn main() {}

View file

@ -119,5 +119,23 @@ LL | pub fn len(&self) -> Result<usize, ()> {
| |
= help: use a custom `Error` type instead = help: use a custom `Error` type instead
error: aborting due to 12 previous errors error: struct `AsyncLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
--> $DIR/len_without_is_empty.rs:292:5
|
LL | pub async fn len(&self) -> usize {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: struct `AsyncOptionLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
--> $DIR/len_without_is_empty.rs:304:5
|
LL | pub async fn len(&self) -> Option<usize> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: struct `AsyncResultLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
--> $DIR/len_without_is_empty.rs:325:5
|
LL | pub async fn len(&self) -> Result<usize, ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 15 previous errors

View file

@ -175,3 +175,7 @@ fn attributes() {
#[expect(clippy::let_unit_value)] #[expect(clippy::let_unit_value)]
let _ = f(); let _ = f();
} }
async fn issue10433() {
let _pending: () = std::future::pending().await;
}

View file

@ -175,3 +175,7 @@ fn attributes() {
#[expect(clippy::let_unit_value)] #[expect(clippy::let_unit_value)]
let _ = f(); let _ = f();
} }
async fn issue10433() {
let _pending: () = std::future::pending().await;
}

View file

@ -0,0 +1,19 @@
#![allow(unused)]
#![warn(clippy::let_with_type_underscore)]
#![allow(clippy::let_unit_value)]
fn func() -> &'static str {
""
}
fn main() {
// Will lint
let x: _ = 1;
let _: _ = 2;
let x: _ = func();
let x = 1; // Will not lint, Rust inferres this to an integer before Clippy
let x = func();
let x: Vec<_> = Vec::<u32>::new();
let x: [_; 1] = [1];
}

View file

@ -0,0 +1,39 @@
error: variable declared with type underscore
--> $DIR/let_with_type_underscore.rs:11:5
|
LL | let x: _ = 1;
| ^^^^^^^^^^^^^
|
help: remove the explicit type `_` declaration
--> $DIR/let_with_type_underscore.rs:11:10
|
LL | let x: _ = 1;
| ^^^
= note: `-D clippy::let-with-type-underscore` implied by `-D warnings`
error: variable declared with type underscore
--> $DIR/let_with_type_underscore.rs:12:5
|
LL | let _: _ = 2;
| ^^^^^^^^^^^^^
|
help: remove the explicit type `_` declaration
--> $DIR/let_with_type_underscore.rs:12:10
|
LL | let _: _ = 2;
| ^^^
error: variable declared with type underscore
--> $DIR/let_with_type_underscore.rs:13:5
|
LL | let x: _ = func();
| ^^^^^^^^^^^^^^^^^^
|
help: remove the explicit type `_` declaration
--> $DIR/let_with_type_underscore.rs:13:10
|
LL | let x: _ = func();
| ^^^
error: aborting due to 3 previous errors

View file

@ -2,6 +2,7 @@
// aux-build:macro_rules.rs // aux-build:macro_rules.rs
#![warn(clippy::manual_rem_euclid)] #![warn(clippy::manual_rem_euclid)]
#![allow(clippy::let_with_type_underscore)]
#[macro_use] #[macro_use]
extern crate macro_rules; extern crate macro_rules;

View file

@ -2,6 +2,7 @@
// aux-build:macro_rules.rs // aux-build:macro_rules.rs
#![warn(clippy::manual_rem_euclid)] #![warn(clippy::manual_rem_euclid)]
#![allow(clippy::let_with_type_underscore)]
#[macro_use] #[macro_use]
extern crate macro_rules; extern crate macro_rules;

View file

@ -1,5 +1,5 @@
error: manual `rem_euclid` implementation error: manual `rem_euclid` implementation
--> $DIR/manual_rem_euclid.rs:19:18 --> $DIR/manual_rem_euclid.rs:20:18
| |
LL | let _: i32 = ((value % 4) + 4) % 4; LL | let _: i32 = ((value % 4) + 4) % 4;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
@ -7,31 +7,31 @@ LL | let _: i32 = ((value % 4) + 4) % 4;
= note: `-D clippy::manual-rem-euclid` implied by `-D warnings` = note: `-D clippy::manual-rem-euclid` implied by `-D warnings`
error: manual `rem_euclid` implementation error: manual `rem_euclid` implementation
--> $DIR/manual_rem_euclid.rs:20:18 --> $DIR/manual_rem_euclid.rs:21:18
| |
LL | let _: i32 = (4 + (value % 4)) % 4; LL | let _: i32 = (4 + (value % 4)) % 4;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
error: manual `rem_euclid` implementation error: manual `rem_euclid` implementation
--> $DIR/manual_rem_euclid.rs:21:18 --> $DIR/manual_rem_euclid.rs:22:18
| |
LL | let _: i32 = (value % 4 + 4) % 4; LL | let _: i32 = (value % 4 + 4) % 4;
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
error: manual `rem_euclid` implementation error: manual `rem_euclid` implementation
--> $DIR/manual_rem_euclid.rs:22:18 --> $DIR/manual_rem_euclid.rs:23:18
| |
LL | let _: i32 = (4 + value % 4) % 4; LL | let _: i32 = (4 + value % 4) % 4;
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
error: manual `rem_euclid` implementation error: manual `rem_euclid` implementation
--> $DIR/manual_rem_euclid.rs:23:22 --> $DIR/manual_rem_euclid.rs:24:22
| |
LL | let _: i32 = 1 + (4 + value % 4) % 4; LL | let _: i32 = 1 + (4 + value % 4) % 4;
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
error: manual `rem_euclid` implementation error: manual `rem_euclid` implementation
--> $DIR/manual_rem_euclid.rs:12:22 --> $DIR/manual_rem_euclid.rs:13:22
| |
LL | let _: i32 = ((value % 4) + 4) % 4; LL | let _: i32 = ((value % 4) + 4) % 4;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
@ -42,25 +42,25 @@ LL | internal_rem_euclid!();
= note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info)
error: manual `rem_euclid` implementation error: manual `rem_euclid` implementation
--> $DIR/manual_rem_euclid.rs:49:5 --> $DIR/manual_rem_euclid.rs:50:5
| |
LL | ((num % 4) + 4) % 4 LL | ((num % 4) + 4) % 4
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
error: manual `rem_euclid` implementation error: manual `rem_euclid` implementation
--> $DIR/manual_rem_euclid.rs:54:5 --> $DIR/manual_rem_euclid.rs:55:5
| |
LL | ((num % 4) + 4) % 4 LL | ((num % 4) + 4) % 4
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
error: manual `rem_euclid` implementation error: manual `rem_euclid` implementation
--> $DIR/manual_rem_euclid.rs:66:18 --> $DIR/manual_rem_euclid.rs:67:18
| |
LL | let _: i32 = ((x % 4) + 4) % 4; LL | let _: i32 = ((x % 4) + 4) % 4;
| ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
error: manual `rem_euclid` implementation error: manual `rem_euclid` implementation
--> $DIR/manual_rem_euclid.rs:79:18 --> $DIR/manual_rem_euclid.rs:80:18
| |
LL | let _: i32 = ((x % 4) + 4) % 4; LL | let _: i32 = ((x % 4) + 4) % 4;
| ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`

View file

@ -16,7 +16,7 @@ fn str_to_int_ok(x: &str) -> i32 {
#[rustfmt::skip] #[rustfmt::skip]
fn strange_some_no_else(x: &str) -> i32 { fn strange_some_no_else(x: &str) -> i32 {
{ {
if let Ok(y) = x . parse() { if let Ok(y) = x . parse() {
return y; return y;
}; };
0 0

View file

@ -18,7 +18,7 @@ LL | if let Some(y) = x . parse() . ok () {
| |
help: consider matching on `Ok(y)` and removing the call to `ok` instead help: consider matching on `Ok(y)` and removing the call to `ok` instead
| |
LL | if let Ok(y) = x . parse() { LL | if let Ok(y) = x . parse() {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: matching on `Some` with `ok()` is redundant error: matching on `Some` with `ok()` is redundant

View file

@ -0,0 +1,84 @@
#![allow(unused)]
#![warn(clippy::missing_assert_message)]
macro_rules! bar {
($( $x:expr ),*) => {
foo()
};
}
fn main() {}
// Should trigger warning
fn asserts_without_message() {
assert!(foo());
assert_eq!(foo(), foo());
assert_ne!(foo(), foo());
debug_assert!(foo());
debug_assert_eq!(foo(), foo());
debug_assert_ne!(foo(), foo());
}
// Should trigger warning
fn asserts_without_message_but_with_macro_calls() {
assert!(bar!(true));
assert!(bar!(true, false));
assert_eq!(bar!(true), foo());
assert_ne!(bar!(true, true), bar!(true));
}
// Should trigger warning
fn asserts_with_trailing_commas() {
assert!(foo(),);
assert_eq!(foo(), foo(),);
assert_ne!(foo(), foo(),);
debug_assert!(foo(),);
debug_assert_eq!(foo(), foo(),);
debug_assert_ne!(foo(), foo(),);
}
// Should not trigger warning
fn asserts_with_message_and_with_macro_calls() {
assert!(bar!(true), "msg");
assert!(bar!(true, false), "msg");
assert_eq!(bar!(true), foo(), "msg");
assert_ne!(bar!(true, true), bar!(true), "msg");
}
// Should not trigger warning
fn asserts_with_message() {
assert!(foo(), "msg");
assert_eq!(foo(), foo(), "msg");
assert_ne!(foo(), foo(), "msg");
debug_assert!(foo(), "msg");
debug_assert_eq!(foo(), foo(), "msg");
debug_assert_ne!(foo(), foo(), "msg");
}
// Should not trigger warning
#[test]
fn asserts_without_message_but_inside_a_test_function() {
assert!(foo());
assert_eq!(foo(), foo());
assert_ne!(foo(), foo());
debug_assert!(foo());
debug_assert_eq!(foo(), foo());
debug_assert_ne!(foo(), foo());
}
// Should not trigger warning
#[cfg(test)]
mod tests {
fn asserts_without_message_but_inside_a_test_module() {
assert!(foo());
assert_eq!(foo(), foo());
assert_ne!(foo(), foo());
debug_assert!(foo());
debug_assert_eq!(foo(), foo());
debug_assert_ne!(foo(), foo());
}
}
fn foo() -> bool {
true
}

View file

@ -0,0 +1,131 @@
error: assert without any message
--> $DIR/missing_assert_message.rs:14:5
|
LL | assert!(foo());
| ^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
= note: `-D clippy::missing-assert-message` implied by `-D warnings`
error: assert without any message
--> $DIR/missing_assert_message.rs:15:5
|
LL | assert_eq!(foo(), foo());
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:16:5
|
LL | assert_ne!(foo(), foo());
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:17:5
|
LL | debug_assert!(foo());
| ^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:18:5
|
LL | debug_assert_eq!(foo(), foo());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:19:5
|
LL | debug_assert_ne!(foo(), foo());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:24:5
|
LL | assert!(bar!(true));
| ^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:25:5
|
LL | assert!(bar!(true, false));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:26:5
|
LL | assert_eq!(bar!(true), foo());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:27:5
|
LL | assert_ne!(bar!(true, true), bar!(true));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:32:5
|
LL | assert!(foo(),);
| ^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:33:5
|
LL | assert_eq!(foo(), foo(),);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:34:5
|
LL | assert_ne!(foo(), foo(),);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:35:5
|
LL | debug_assert!(foo(),);
| ^^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:36:5
|
LL | debug_assert_eq!(foo(), foo(),);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: assert without any message
--> $DIR/missing_assert_message.rs:37:5
|
LL | debug_assert_ne!(foo(), foo(),);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider describing why the failing assert is problematic
error: aborting due to 16 previous errors

View file

@ -6,30 +6,12 @@ LL | type Typedef = String;
| |
= note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
error: missing documentation for a type alias
--> $DIR/missing_doc.rs:17:1
|
LL | pub type PubTypedef = String;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a module error: missing documentation for a module
--> $DIR/missing_doc.rs:19:1 --> $DIR/missing_doc.rs:19:1
| |
LL | mod module_no_dox {} LL | mod module_no_dox {}
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a module
--> $DIR/missing_doc.rs:20:1
|
LL | pub mod pub_module_no_dox {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a function
--> $DIR/missing_doc.rs:24:1
|
LL | pub fn foo2() {}
| ^^^^^^^^^^^^^^^^
error: missing documentation for a function error: missing documentation for a function
--> $DIR/missing_doc.rs:25:1 --> $DIR/missing_doc.rs:25:1
| |
@ -69,50 +51,18 @@ error: missing documentation for a variant
LL | BarB, LL | BarB,
| ^^^^ | ^^^^
error: missing documentation for an enum
--> $DIR/missing_doc.rs:44:1
|
LL | / pub enum PubBaz {
LL | | PubBazA { a: isize },
LL | | }
| |_^
error: missing documentation for a variant
--> $DIR/missing_doc.rs:45:5
|
LL | PubBazA { a: isize },
| ^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a struct field
--> $DIR/missing_doc.rs:45:15
|
LL | PubBazA { a: isize },
| ^^^^^^^^
error: missing documentation for a constant error: missing documentation for a constant
--> $DIR/missing_doc.rs:65:1 --> $DIR/missing_doc.rs:65:1
| |
LL | const FOO: u32 = 0; LL | const FOO: u32 = 0;
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
error: missing documentation for a constant
--> $DIR/missing_doc.rs:72:1
|
LL | pub const FOO4: u32 = 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a static error: missing documentation for a static
--> $DIR/missing_doc.rs:74:1 --> $DIR/missing_doc.rs:74:1
| |
LL | static BAR: u32 = 0; LL | static BAR: u32 = 0;
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a static
--> $DIR/missing_doc.rs:81:1
|
LL | pub static BAR4: u32 = 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a module error: missing documentation for a module
--> $DIR/missing_doc.rs:83:1 --> $DIR/missing_doc.rs:83:1
| |
@ -125,35 +75,17 @@ LL | | }
LL | | } LL | | }
| |_^ | |_^
error: missing documentation for a function
--> $DIR/missing_doc.rs:86:5
|
LL | pub fn undocumented1() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a function
--> $DIR/missing_doc.rs:87:5
|
LL | pub fn undocumented2() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a function error: missing documentation for a function
--> $DIR/missing_doc.rs:88:5 --> $DIR/missing_doc.rs:88:5
| |
LL | fn undocumented3() {} LL | fn undocumented3() {}
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a function
--> $DIR/missing_doc.rs:93:9
|
LL | pub fn also_undocumented1() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a function error: missing documentation for a function
--> $DIR/missing_doc.rs:94:9 --> $DIR/missing_doc.rs:94:9
| |
LL | fn also_undocumented2() {} LL | fn also_undocumented2() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 24 previous errors error: aborting due to 13 previous errors

View file

@ -21,60 +21,12 @@ error: missing documentation for a struct field
LL | b: isize, LL | b: isize,
| ^^^^^^^^ | ^^^^^^^^
error: missing documentation for a struct
--> $DIR/missing_doc_impl.rs:18:1
|
LL | / pub struct PubFoo {
LL | | pub a: isize,
LL | | b: isize,
LL | | }
| |_^
error: missing documentation for a struct field
--> $DIR/missing_doc_impl.rs:19:5
|
LL | pub a: isize,
| ^^^^^^^^^^^^
error: missing documentation for a struct field error: missing documentation for a struct field
--> $DIR/missing_doc_impl.rs:20:5 --> $DIR/missing_doc_impl.rs:20:5
| |
LL | b: isize, LL | b: isize,
| ^^^^^^^^ | ^^^^^^^^
error: missing documentation for a trait
--> $DIR/missing_doc_impl.rs:43:1
|
LL | / pub trait C {
LL | | fn foo(&self);
LL | | fn foo_with_impl(&self) {}
LL | | }
| |_^
error: missing documentation for a method
--> $DIR/missing_doc_impl.rs:44:5
|
LL | fn foo(&self);
| ^^^^^^^^^^^^^^
error: missing documentation for a method
--> $DIR/missing_doc_impl.rs:45:5
|
LL | fn foo_with_impl(&self) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for an associated type
--> $DIR/missing_doc_impl.rs:55:5
|
LL | type AssociatedType;
| ^^^^^^^^^^^^^^^^^^^^
error: missing documentation for an associated type
--> $DIR/missing_doc_impl.rs:56:5
|
LL | type AssociatedTypeDef = Self;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for an associated function error: missing documentation for an associated function
--> $DIR/missing_doc_impl.rs:67:5 --> $DIR/missing_doc_impl.rs:67:5
| |
@ -89,12 +41,6 @@ error: missing documentation for an associated function
LL | fn bar() {} LL | fn bar() {}
| ^^^^^^^^^^^ | ^^^^^^^^^^^
error: missing documentation for an associated function
--> $DIR/missing_doc_impl.rs:74:5
|
LL | pub fn foo() {}
| ^^^^^^^^^^^^^^^
error: missing documentation for an associated function error: missing documentation for an associated function
--> $DIR/missing_doc_impl.rs:78:5 --> $DIR/missing_doc_impl.rs:78:5
| |
@ -103,5 +49,5 @@ LL | | 1
LL | | } LL | | }
| |_____^ | |_____^
error: aborting due to 15 previous errors error: aborting due to 7 previous errors

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