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

This commit is contained in:
Philipp Krones 2023-03-10 10:16:08 +01:00
commit baa997caf6
No known key found for this signature in database
GPG Key ID: 1CA0DF2AF59D68A5
113 changed files with 2321 additions and 723 deletions

View File

@ -6,7 +6,152 @@ document.
## 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 beta, 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
@ -4307,6 +4452,7 @@ Released 2018-09-13
[`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_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_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
@ -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_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_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
[`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
@ -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
[`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
[`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_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
@ -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
[`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_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_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

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
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
lints independant of the compiler's AST / HIR data structures**.
lints independent of the compiler's AST / HIR data structures**.
# Approach
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
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
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
@ -270,7 +270,7 @@ pattern!{
// matches if expressions that **may or may not** 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 | 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 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
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.
@ -717,7 +717,7 @@ if false {
#### Problems
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
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.
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
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
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
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
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).
#### Functional composition

View File

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

View File

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

View File

@ -85,8 +85,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
);
}
} else {
let span =
block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
if span.from_expansion() || expr.span.from_expansion() {
return;
}

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
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 if_chain::if_chain;
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 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);
let ctxt = expr.span.ctxt();
if cast_expr.span.ctxt() == ctxt;
then {
let func = match rpk {
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 mut applicability = Applicability::MachineApplicable;
let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability);
let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability);
let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0;
let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0;
span_lint_and_sugg(
cx,
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 @@
crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
crate::collapsible_if::COLLAPSIBLE_ELSE_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::copies::BRANCHES_SHARING_CODE_INFO,
crate::copies::IFS_SAME_COND_INFO,
@ -226,6 +227,7 @@
crate::let_underscore::LET_UNDERSCORE_LOCK_INFO,
crate::let_underscore::LET_UNDERSCORE_MUST_USE_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::NEEDLESS_LIFETIMES_INFO,
crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,
@ -416,6 +418,7 @@
crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO,
crate::misc_early::ZERO_PREFIXED_LITERAL_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_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
@ -517,6 +520,7 @@
crate::ranges::REVERSED_EMPTY_RANGES_INFO,
crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_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_closure_call::REDUNDANT_CLOSURE_CALL_INFO,
crate::redundant_else::REDUNDANT_ELSE_INFO,

View File

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

View File

@ -1357,10 +1357,10 @@ fn replace_types<'tcx>(
&& let Some(term_ty) = projection_predicate.term.ty()
&& let ty::Param(term_param_ty) = term_ty.kind()
{
let item_def_id = projection_predicate.projection_ty.def_id;
let assoc_item = cx.tcx.associated_item(item_def_id);
let projection = cx.tcx
.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, []));
let projection = cx.tcx.mk_ty_from_kind(ty::Alias(
ty::Projection,
projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty),
));
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)

View File

@ -8,7 +8,7 @@
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind,
};
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_span::sym;
@ -81,13 +81,18 @@ fn check_struct<'tcx>(
self_ty: &Ty<'_>,
func_expr: &Expr<'_>,
adt_def: AdtDef<'_>,
substs: SubstsRef<'_>,
) {
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
for arg in a.args {
if !matches!(arg, GenericArg::Lifetime(_)) {
return;
}
if let Some(PathSegment { args, .. }) = p.segments.last() {
let args = args.map(|a| a.args).unwrap_or(&[]);
// 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 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
if let ImplItemKind::Fn(_, b) = &impl_item.kind;
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 !attrs.iter().any(|attr| attr.doc_str().is_some());
if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
@ -192,7 +197,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
then {
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) {
check_enum(cx, item, func_expr, adt_def);
}

View File

@ -11,7 +11,7 @@
///
/// ### Why is this bad?
/// 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
/// the main function.
///

View File

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

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
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 if_chain::if_chain;
use rustc_errors::Applicability;
@ -84,9 +84,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
_ => false,
};
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 {
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())
};
span_useless_format(cx, call_site, sugg, applicability);

View File

@ -367,12 +367,7 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
}
}
fn check_format_in_format_args(
cx: &LateContext<'_>,
call_site: Span,
name: Symbol,
arg: &Expr<'_>,
) {
fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
let expn_data = arg.span.ctxt().outer_expn_data();
if expn_data.call_site.from_expansion() {
return;
@ -445,7 +440,10 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
/// Returns true if `hir_id` is referred to by multiple format params
fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err()
args.params()
.filter(|param| param.value.hir_id == hir_id)
.at_most_one()
.is_err()
}
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
@ -455,7 +453,11 @@ fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tc
let mut n_total = 0;
let mut n_needed = 0;
loop {
if let Some(Adjustment { kind: Adjust::Deref(overloaded_deref), target }) = iter.next() {
if let Some(Adjustment {
kind: Adjust::Deref(overloaded_deref),
target,
}) = iter.next()
{
n_total += 1;
if overloaded_deref.is_some() {
n_needed = n_total;

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() {
diag.span_suggestion_with_style(
gen_span,
"add a type paremeter",
"add a type parameter",
format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]),
rustc_errors::Applicability::HasPlaceholders,
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.parent(),
),
"add a type paremeter",
"add a type parameter",
format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
rustc_errors::Applicability::HasPlaceholders,
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 {
// 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;
};

View File

@ -185,7 +185,7 @@
/// ### Examples
/// ```rust
/// // 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"]
pub MUST_USE_CANDIDATE,

View File

@ -1,7 +1,7 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
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 rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability;
@ -55,6 +55,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let ExprKind::AssignOp(op1, target, value) = ex.kind;
let ty = cx.typeck_results().expr_ty(target);
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 BinOpKind::Add == op1.node;
if let ExprKind::Lit(ref lit) = value.kind;
@ -62,8 +65,15 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if block.expr.is_none();
then {
let mut app = Applicability::MachineApplicable;
let code = snippet_with_applicability(cx, target.span, "_", &mut app);
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 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);")
};
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::msrvs::{self, Msrv};
use clippy_utils::source;
use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty;
use rustc_errors::Applicability;
@ -161,14 +161,9 @@ fn print_unchecked_duration_subtraction_sugg(
) {
let mut applicability = Applicability::MachineApplicable;
let left_expr =
source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability);
let right_expr = source::snippet_with_applicability(
cx,
right_expr.span,
"std::time::Duration::from_secs(1)",
&mut applicability,
);
let ctxt = expr.span.ctxt();
let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "<instant>", &mut applicability).0;
let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "<duration>", &mut applicability).0;
diagnostics::span_lint_and_sugg(
cx,

View File

@ -1,13 +1,14 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
use clippy_utils::source::snippet_with_context;
use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators, sugg::Sugg};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefIdSet;
use rustc_hir::{
def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item,
ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp,
def::Res, def_id::DefId, lang_items::LangItem, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg,
GenericBound, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, PathSegment, PrimTy,
QPath, TraitItemRef, TyKind, TypeBindingKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
@ -16,7 +17,6 @@
source_map::{Span, Spanned, Symbol},
symbol::sym,
};
use std::borrow::Cow;
declare_clippy_lint! {
/// ### What it does
@ -251,33 +251,98 @@ fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) {
}
#[derive(Debug, Clone, Copy)]
enum LenOutput<'tcx> {
enum LenOutput {
Integral,
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() {
ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
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()))
},
ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => subs
.type_at(0)
.is_integral()
.then(|| LenOutput::Result(adt.did(), subs.type_at(1))),
ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => {
subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did()))
},
_ => None,
}
}
impl<'tcx> LenOutput<'tcx> {
fn matches_is_empty_output(self, ty: Ty<'tcx>) -> bool {
impl LenOutput {
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()) {
(_, &ty::Bool) => true,
(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() => {
subs.type_at(0).is_bool() && subs.type_at(1) == err_ty
},
(Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
_ => false,
}
}
@ -301,9 +366,14 @@ fn expected_sig(self, self_kind: ImplicitSelfKind) -> String {
}
/// 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 {
[arg, res] if len_output.matches_is_empty_output(*res) => {
[arg, res] if len_output.matches_is_empty_output(cx, *res) => {
matches!(
(arg.kind(), self_kind),
(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.
fn check_for_is_empty<'tcx>(
cx: &LateContext<'tcx>,
fn check_for_is_empty(
cx: &LateContext<'_>,
span: Span,
self_kind: ImplicitSelfKind,
output: LenOutput<'tcx>,
output: LenOutput,
impl_ty: DefId,
item_name: Symbol,
item_kind: &str,
@ -352,6 +422,7 @@ fn check_for_is_empty<'tcx>(
Some(is_empty)
if !(is_empty.fn_has_self_parameter
&& check_is_empty_sig(
cx,
cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(),
self_kind,
output,
@ -431,7 +502,7 @@ fn check_len(
&format!("using `{op}is_empty` is clearer and more explicit"),
format!(
"{op}{}.is_empty()",
snippet_with_applicability(cx, receiver.span, "_", &mut applicability)
snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0,
),
applicability,
);
@ -444,13 +515,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex
let mut applicability = Applicability::MachineApplicable;
let lit1 = peel_ref_operators(cx, lit1);
let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability);
// 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})"));
}
let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_par();
span_lint_and_sugg(
cx,

View File

@ -124,7 +124,7 @@
/// ```
#[clippy::version = "1.69.0"]
pub LET_UNDERSCORE_UNTYPED,
pedantic,
restriction,
"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 checked_conversions;
mod cognitive_complexity;
mod collapsible_if;
mod collection_is_never_read;
mod comparison_chain;
mod copies;
mod copy_iterator;
@ -166,6 +167,7 @@
mod len_zero;
mod let_if_seq;
mod let_underscore;
mod let_with_type_underscore;
mod lifetimes;
mod literal_representation;
mod loops;
@ -192,6 +194,7 @@
mod misc;
mod misc_early;
mod mismatching_type_param_order;
mod missing_assert_message;
mod missing_const_for_fn;
mod missing_doc;
mod missing_enforced_import_rename;
@ -249,6 +252,7 @@
mod ranges;
mod rc_clone_in_vec_init;
mod read_zero_byte_vec;
mod redundant_async_block;
mod redundant_clone;
mod redundant_closure_call;
mod redundant_else;
@ -924,6 +928,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(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`
}

View File

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

View File

@ -1,11 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::get_parent_expr;
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_errors::Applicability;
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_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
@ -55,13 +56,17 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind;
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 matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
if let ExprKind::Lit(lit) = &other_expr.kind;
if let LitKind::Int(8, _) = lit.node;
then {
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"));
span_lint_and_sugg(

View File

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

View File

@ -1,7 +1,7 @@
use clippy_utils::consts::{constant_full_int, FullInt};
use clippy_utils::diagnostics::span_lint_and_sugg;
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 rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
@ -60,12 +60,16 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
return;
}
// (x % c + c) % c
if let ExprKind::Binary(op1, expr1, right) = expr.kind
&& op1.node == BinOpKind::Rem
&& let ctxt = expr.span.ctxt()
&& expr1.span.ctxt() == ctxt
&& let Some(const1) = check_for_unsigned_int_constant(cx, right)
&& let ExprKind::Binary(op2, left, right) = expr1.kind
&& op2.node == BinOpKind::Add
&& 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
&& op3.node == BinOpKind::Rem
&& let Some(const3) = check_for_unsigned_int_constant(cx, right)
@ -86,7 +90,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
};
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(
cx,
MANUAL_REM_EUCLID,

View File

@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher;
use clippy_utils::method_chain_args;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::is_res_lang_ctor;
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
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_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
@ -58,17 +58,18 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
};
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 PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = 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 is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result);
if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
if let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind; //get operation
if ok_path.ident.as_str() == "ok";
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
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 {
let mut applicability = Applicability::MachineApplicable;
let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_path.ident.span), "", &mut applicability);
let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0;
let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0;
let sugg = format!(
"{ifwhile} let Ok({some_expr_string}) = {}",
trimmed_ok.trim().trim_end_matches('.'),

View File

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

View File

@ -7,7 +7,7 @@
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::{Symbol, sym};
use rustc_span::symbol::{sym, Symbol};
use super::INEFFICIENT_TO_STRING;

View File

@ -340,8 +340,9 @@
declare_clippy_lint! {
/// ### What it does
/// Checks for methods with certain name prefixes and which
/// doesn't match how self is taken. The actual rules are:
/// Checks for methods with certain name prefixes or suffixes, and which
/// do not adhere to standard conventions regarding how `self` is taken.
/// The actual rules are:
///
/// |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::diagnostics::span_lint;
use clippy_utils::is_from_proc_macro;
use hir::def_id::LocalDefId;
use if_chain::if_chain;
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::Visibility;
use rustc_session::{declare_tool_lint, impl_lint_pass};
@ -21,8 +21,7 @@
declare_clippy_lint! {
/// ### What it does
/// Warns if there is missing doc for any documentable item
/// (public or private).
/// Warns if there is missing doc for any private documentable item
///
/// ### Why is this bad?
/// Doc is good. *rustc* has a `MISSING_DOCS`
@ -32,7 +31,7 @@
#[clippy::version = "pre 1.29.0"]
pub MISSING_DOCS_IN_PRIVATE_ITEMS,
restriction,
"detects missing documentation for public and private members"
"detects missing documentation for private members"
}
pub struct MissingDoc {
@ -107,11 +106,14 @@ fn check_missing_docs_attrs(
if vis == Visibility::Public || vis != Visibility::Restricted(CRATE_DEF_ID.into()) {
return;
}
} else if def_id != CRATE_DEF_ID && cx.effective_visibilities.is_exported(def_id) {
return;
}
let has_doc = attrs
.iter()
.any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
if !has_doc {
span_lint(
cx,

View File

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

View File

@ -92,10 +92,6 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
self.found = true;
return;
},
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj

View File

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

View File

@ -53,6 +53,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|| is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder)))
|| (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS));
if let ExprKind::Lit(_) = param.kind;
if param.span.ctxt() == expr.span.ctxt();
then {
let Some(snip) = snippet_opt(cx, param.span) else {
@ -71,6 +72,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
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 let ExprKind::Lit(_) = param.kind;
if param.span.ctxt() == expr.span.ctxt();
if let Some(snip) = snippet_opt(cx, param.span);
if !snip.starts_with("0o");
then {

View File

@ -143,6 +143,10 @@ fn manage_bin_ops<'tcx>(
return;
}
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_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs);
match (
@ -151,10 +155,13 @@ fn manage_bin_ops<'tcx>(
) {
(None, None) => false,
(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::Div | hir::BinOpKind::Rem, _)
| (hir::BinOpKind::Mul, 0 | 1) => true,
// Multiplication by 1 or 0 will never overflow
| (hir::BinOpKind::Mul, 0 | 1)
=> true,
_ => false,
},
(Some(_), Some(_)) => {

View File

@ -21,7 +21,7 @@
/// let mut permissions = metadata.permissions();
/// permissions.set_readonly(false);
/// ```
#[clippy::version = "1.66.0"]
#[clippy::version = "1.68.0"]
pub PERMISSIONS_SET_READONLY_FALSE,
suspicious,
"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

@ -55,11 +55,11 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
if matches!(cx.tcx.def_kind(id.owner_id), DefKind::Impl { .. })
&& let item = cx.tcx.hir().item(id)
&& let ItemKind::Impl(Impl {
items,
of_trait,
self_ty,
..
}) = &item.kind
items,
of_trait,
self_ty,
..
}) = &item.kind
&& let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
{
if !map.contains_key(res) {

View File

@ -45,7 +45,7 @@
/// }
/// }
/// ```
#[clippy::version = "1.67.0"]
#[clippy::version = "1.68.0"]
pub SIZE_OF_REF,
suspicious,
"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::source::snippet_with_applicability;
use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core};
@ -10,6 +10,7 @@
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
use rustc_span::SyntaxContext;
use rustc_span::{sym, symbol::Ident, Span};
declare_clippy_lint! {
@ -80,43 +81,45 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
}
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;
if !can_mut_borrow_both(cx, e1, e2) {
if let ExprKind::Index(lhs1, idx1) = e1.kind {
if let ExprKind::Index(lhs2, idx2) = e2.kind {
if eq_expr_value(cx, lhs1, lhs2) {
let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
if let ExprKind::Index(lhs1, idx1) = e1.kind
&& let ExprKind::Index(lhs2, idx2) = e2.kind
&& eq_expr_value(cx, lhs1, lhs2)
&& e1.span.ctxt() == ctxt
&& e2.span.ctxt() == ctxt
{
let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
if matches!(ty.kind(), ty::Slice(_))
|| matches!(ty.kind(), ty::Array(_, _))
|| is_type_diagnostic_item(cx, ty, sym::Vec)
|| is_type_diagnostic_item(cx, ty, sym::VecDeque)
{
let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
span_lint_and_sugg(
cx,
MANUAL_SWAP,
span,
&format!("this looks like you are swapping elements of `{slice}` manually"),
"try",
format!(
"{}.swap({}, {})",
slice.maybe_par(),
snippet_with_applicability(cx, idx1.span, "..", &mut applicability),
snippet_with_applicability(cx, idx2.span, "..", &mut applicability),
),
applicability,
);
}
}
if matches!(ty.kind(), ty::Slice(_))
|| matches!(ty.kind(), ty::Array(_, _))
|| is_type_diagnostic_item(cx, ty, sym::Vec)
|| is_type_diagnostic_item(cx, ty, sym::VecDeque)
{
let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
span_lint_and_sugg(
cx,
MANUAL_SWAP,
span,
&format!("this looks like you are swapping elements of `{slice}` manually"),
"try",
format!(
"{}.swap({}, {});",
slice.maybe_par(),
snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0,
snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0,
),
applicability,
);
}
}
return;
}
let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability);
let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability);
let first = Sugg::hir_with_context(cx, e1, ctxt, "..", &mut applicability);
let second = Sugg::hir_with_context(cx, e2, ctxt, "..", &mut applicability);
let Some(sugg) = std_or_core(cx) else { return };
span_lint_and_then(
@ -128,7 +131,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
diag.span_suggestion(
span,
"try",
format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()),
format!("{sugg}::mem::swap({}, {});", first.mut_addr(), second.mut_addr()),
applicability,
);
if !is_xor_based {
@ -144,19 +147,19 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
return;
}
for w in block.stmts.windows(3) {
for [s1, s2, s3] in block.stmts.array_windows::<3>() {
if_chain! {
// 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 PatKind::Binding(.., ident, None) = tmp.pat.kind;
// 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;
// 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::Path(QPath::Resolved(None, rhs2)) = rhs2.kind;
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 eq_expr_value(cx, tmp_init, lhs1);
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 {
let span = w[0].span.to(second.span);
let span = s1.span.to(s3.span);
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.
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 let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(&window[0]);
if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(&window[1]);
if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(&window[2]);
let ctxt = s1.span.ctxt();
if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt);
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, lhs2, rhs1);
if eq_expr_value(cx, lhs1, rhs0);
if eq_expr_value(cx, lhs1, rhs2);
if s2.span.ctxt() == ctxt;
if s3.span.ctxt() == ctxt;
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);
}
};
@ -264,9 +277,12 @@ fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
}
/// 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>)> {
if let StmtKind::Semi(expr) = stmt.kind {
if let ExprKind::AssignOp(
fn extract_sides_of_xor_assign<'a, 'hir>(
stmt: &'a Stmt<'hir>,
ctxt: SyntaxContext,
) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
if let StmtKind::Semi(expr) = stmt.kind
&& let ExprKind::AssignOp(
Spanned {
node: BinOpKind::BitXor,
..
@ -274,9 +290,10 @@ fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Ex
lhs,
rhs,
) = expr.kind
{
return Some((lhs, rhs));
}
&& expr.span.ctxt() == ctxt
{
Some((lhs, rhs))
} else {
None
}
None
}

View File

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

View File

@ -5,7 +5,7 @@
use core::ops::ControlFlow;
use rustc_errors::Applicability;
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_middle::lint::in_external_macro;
use rustc_middle::ty;
@ -41,6 +41,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
);
}
} else {
if let ExprKind::Match(_, _, MatchSource::AwaitDesugar) = init.kind {
return
}
span_lint_and_then(
cx,
LET_UNIT_VALUE,

View File

@ -10,8 +10,8 @@
def::{CtorOf, DefKind, Res},
def_id::LocalDefId,
intravisit::{walk_inf, walk_ty, Visitor},
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath,
TyKind,
Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item,
ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass};
@ -96,7 +96,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
// 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
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;
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
if parameters.as_ref().map_or(true, |params| {
@ -105,10 +105,17 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
if !item.span.from_expansion();
if !is_from_proc_macro(cx, item); // expensive, should be last check
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 {
impl_id: item.owner_id.def_id,
in_body: 0,
types_to_skip: std::iter::once(self_ty.hir_id).collect(),
types_to_skip,
}
} else {
StackItem::NoCheck

View File

@ -588,7 +588,7 @@ macro_rules! kind {
},
}
},
ExprKind::Err(_) => kind!("Err"),
ExprKind::Err(_) => kind!("Err(_)"),
ExprKind::DropTemps(expr) => {
bind!(self, expr);
kind!("DropTemps({expr})");

View File

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

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.
/// `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`].
///
/// This function is expensive and should be used sparingly.

View File

@ -213,6 +213,7 @@ pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool {
matches!(name, sym::assert_macro | sym::debug_assert_macro)
}
#[derive(Debug)]
pub enum PanicExpn<'a> {
/// No arguments - `panic!()`
Empty,
@ -226,10 +227,7 @@ pub enum PanicExpn<'a> {
impl<'a> PanicExpn<'a> {
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)) {
return None;
}
let ExprKind::Call(callee, [arg]) = &expr.kind else { return None };
let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.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() {
"panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
@ -239,6 +237,21 @@ pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
Self::Display(e)
},
"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,
};
Some(result)
@ -251,7 +264,17 @@ pub fn find_assert_args<'a>(
expr: &'a Expr<'a>,
expn: ExpnId,
) -> 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
@ -275,13 +298,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))?,
};
let mut args = ArrayVec::new();
let mut panic_expn = None;
let _: Option<!> = for_each_expr(expr, |e| {
let panic_expn = for_each_expr(expr, |e| {
if args.is_full() {
if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
panic_expn = PanicExpn::parse(cx, e);
match 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) {
args.push(e);
ControlFlow::Continue(Descend::No)

View File

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

View File

@ -8,12 +8,16 @@ repository = "https://github.com/rust-lang/rust-clippy"
categories = ["development-tools"]
edition = "2021"
publish = false
default-run = "lintcheck"
[dependencies]
anyhow = "1.0.69"
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"
flate2 = "1.0"
indicatif = "0.17.3"
rayon = "1.5.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.85"
@ -24,3 +28,11 @@ walkdir = "2.3"
[features]
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 `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
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 std::env;
use std::path::PathBuf;
use clap::Parser;
use std::{num::NonZeroUsize, path::PathBuf};
fn get_clap_config() -> ArgMatches {
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)]
#[derive(Clone, Debug, Parser)]
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,
/// 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,
/// we save the clippy lint results here
pub lintcheck_results_path: PathBuf,
/// Check only a specified package
/// File to save the clippy lint results here
#[clap(skip = "")]
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>,
/// 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,
/// 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>,
/// Indicate if the output should support markdown syntax
/// Change the reports table to use markdown links
#[clap(long)]
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,
}
impl LintcheckConfig {
pub fn new() -> Self {
let clap_config = get_clap_config();
// 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);
let mut config = LintcheckConfig::parse();
// for the path where we save the lint results, get the filename without extension (so for
// wasd.toml, use "wasd"...)
let filename: PathBuf = sources_toml_path.file_stem().unwrap().into();
let lintcheck_results_path = PathBuf::from(format!(
let filename: PathBuf = config.sources_toml_path.file_stem().unwrap().into();
config.lintcheck_results_path = PathBuf::from(format!(
"lintcheck-logs/{}_logs.{}",
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
// use half of that for the physical core count
// by default use a single thread
let max_jobs = match clap_config.get_one::<usize>("threads") {
Some(&0) => {
// automatic choice
// 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,
// look at the --threads arg, if 0 is passed, use the threads count
if config.max_jobs == 0 {
config.max_jobs = if config.fix || config.recursive {
1
} else {
std::thread::available_parallelism().map_or(1, NonZeroUsize::get)
};
};
let lint_filter: Vec<String> = clap_config
.get_many::<String>("filter")
.map(|iter| {
iter.map(|lint_name| {
let mut filter = lint_name.replace('_', "-");
if !filter.starts_with("clippy::") {
filter.insert_str(0, "clippy::");
}
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"),
for lint_name in &mut config.lint_filter {
*lint_name = format!(
"clippy::{}",
lint_name
.strip_prefix("clippy::")
.unwrap_or(lint_name)
.replace('_', "-")
);
}
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

@ -176,7 +176,7 @@ fn display_help() {
--rustc Pass all args to rustc
-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 --`
with:

View File

@ -18,7 +18,7 @@
-V, --version Print version info and exit
--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 --`
with:

View File

@ -7,6 +7,7 @@
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]
use itertools::Itertools;
use std::path::PathBuf;
use std::process::Command;
use test_utils::IS_RUSTC_TEST_SUITE;
@ -19,8 +20,10 @@ fn dogfood_clippy() {
return;
}
let mut failed_packages = Vec::new();
// "" is the root package
for package in &[
for package in [
"",
"clippy_dev",
"clippy_lints",
@ -28,8 +31,16 @@ fn dogfood_clippy() {
"lintcheck",
"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]
@ -71,7 +82,7 @@ fn run_metadata_collection_lint() {
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 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!("stderr: {}", String::from_utf8_lossy(&output.stderr));
assert!(output.status.success());
output.status.success()
}

View File

@ -45,24 +45,32 @@ fn $method(&mut self, _: $lhs) {}
Div, Custom, Custom, div;
Mul, Custom, Custom, mul;
Rem, Custom, Custom, rem;
Shl, Custom, Custom, shl;
Shr, Custom, Custom, shr;
Sub, Custom, Custom, sub;
Add, Custom, &Custom, add;
Div, Custom, &Custom, div;
Mul, Custom, &Custom, mul;
Rem, Custom, &Custom, rem;
Shl, Custom, &Custom, shl;
Shr, Custom, &Custom, shr;
Sub, Custom, &Custom, sub;
Add, &Custom, Custom, add;
Div, &Custom, Custom, div;
Mul, &Custom, Custom, mul;
Rem, &Custom, Custom, rem;
Shl, &Custom, Custom, shl;
Shr, &Custom, Custom, shr;
Sub, &Custom, Custom, sub;
Add, &Custom, &Custom, add;
Div, &Custom, &Custom, div;
Mul, &Custom, &Custom, mul;
Rem, &Custom, &Custom, rem;
Shl, &Custom, &Custom, shl;
Shr, &Custom, &Custom, shr;
Sub, &Custom, &Custom, sub;
);
@ -71,24 +79,32 @@ fn $method(&mut self, _: $lhs) {}
DivAssign, Custom, Custom, div_assign;
MulAssign, Custom, Custom, mul_assign;
RemAssign, Custom, Custom, rem_assign;
ShlAssign, Custom, Custom, shl_assign;
ShrAssign, Custom, Custom, shr_assign;
SubAssign, Custom, Custom, sub_assign;
AddAssign, Custom, &Custom, add_assign;
DivAssign, Custom, &Custom, div_assign;
MulAssign, Custom, &Custom, mul_assign;
RemAssign, Custom, &Custom, rem_assign;
ShlAssign, Custom, &Custom, shl_assign;
ShrAssign, Custom, &Custom, shr_assign;
SubAssign, Custom, &Custom, sub_assign;
AddAssign, &Custom, Custom, add_assign;
DivAssign, &Custom, Custom, div_assign;
MulAssign, &Custom, Custom, mul_assign;
RemAssign, &Custom, Custom, rem_assign;
ShlAssign, &Custom, Custom, shl_assign;
ShrAssign, &Custom, Custom, shr_assign;
SubAssign, &Custom, Custom, sub_assign;
AddAssign, &Custom, &Custom, add_assign;
DivAssign, &Custom, &Custom, div_assign;
MulAssign, &Custom, &Custom, mul_assign;
RemAssign, &Custom, &Custom, rem_assign;
ShlAssign, &Custom, &Custom, shl_assign;
ShrAssign, &Custom, &Custom, shr_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;
@ -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;
// Binary
_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;
// Unary
_n = -_n;

View File

@ -1,5 +1,5 @@
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;
| ^^^^^^^
@ -7,592 +7,640 @@ LL | _n += 1;
= note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
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;
| ^^^^^^^^
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;
| ^^^^^^^
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;
| ^^^^^^^^
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;
| ^^^^^^^
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;
| ^^^^^^^^
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;
| ^^^^^^^
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;
| ^^^^^^^^
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;
| ^^^^^^^
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;
| ^^^^^^^^
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;
| ^^^^^^^^
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;
| ^^^^^^^^^
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;
| ^^^^^^^^
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;
| ^^^^^^^^^
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;
| ^^^^^^^^
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;
| ^^^^^^^^^
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;
| ^^^^^^^^
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;
| ^^^^^^^^^
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;
| ^^^^^^^^
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;
| ^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^
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;
| ^^^^^^^
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;
| ^^^^^^
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;
| ^^^^^^^
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;
| ^^^^^^
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;
| ^^^^^^^
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;
| ^^^^^^
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;
| ^^^^^^^
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;
| ^^^^^^
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;
| ^^^^^^^
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;
| ^^^^^^
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;
| ^^^^^^^
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;
| ^^^^^^
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;
| ^^^^^^^
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;
| ^^^^^^
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;
| ^^^^^^^
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;
| ^^^^^^^^
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;
| ^^^^^^^^
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;
| ^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^
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;
| ^^^^^^^^^^^^^^^^^
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;
| ^^^
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;
| ^^^^
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;
| ^^^^^^^^
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;
| ^^^^^^^^^
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;
| ^^^^^
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;
| ^^^^^
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;
| ^^^^^^^^^^^^^
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;
| ^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:369: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
--> $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:380:5
--> $DIR/arithmetic_side_effects.rs:408:5
|
LL | i -= 1;
| ^^^^^^
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;
| ^^^^^^
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;
| ^^^^^^
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;
| ^^^^^^^^^
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;
| ^^^^^^^^^
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;
| ^^^^^^
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;
| ^^^^^^^^^
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;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $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
error: aborting due to 107 previous errors

View File

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

View File

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

View File

@ -1,5 +1,5 @@
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 {
| _____________________-
@ -19,7 +19,7 @@ LL + }.await
|
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 {
| ____________________-
@ -32,7 +32,7 @@ LL | | };
| |_____- outer async construct
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 || {
| ________________________-
@ -51,7 +51,7 @@ LL + }.await
|
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 || {
| _______________________-
@ -64,7 +64,7 @@ LL | | };
| |_____- outer async construct
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;
| ^^^^^^^^^^^^^^^^
@ -74,7 +74,7 @@ LL | let _l = async || CustomFutureType;
| help: consider awaiting this value: `CustomFutureType.await`
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 || {
| _______________________-

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

@ -2,7 +2,7 @@
//! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details.
#![warn(clippy::use_self)]
#![allow(dead_code)]
#![allow(dead_code, clippy::let_with_type_underscore)]
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::branches_sharing_code,
clippy::match_single_binding,
clippy::let_unit_value
clippy::let_unit_value,
clippy::let_with_type_underscore
)]
#[macro_use]

View File

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

View File

@ -1,5 +1,5 @@
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;
| ^^^^ 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`
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.];
| ^^ help: consider adding suffix: `1.0_f64`
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.];
| ^^ help: consider adding suffix: `2.0_f64`
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.];
| ^^ help: consider adding suffix: `3.0_f64`
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.) };
| ^^ help: consider adding suffix: `1.0_f64`
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.) };
| ^^ help: consider adding suffix: `2.0_f64`
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.) };
| ^^ help: consider adding suffix: `3.0_f64`
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.) };
| ^^ help: consider adding suffix: `4.0_f64`
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. {
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:25:18
--> $DIR/default_numeric_fallback_f64.rs:26:18
|
LL | _ => 1.,
| ^^ help: consider adding suffix: `1.0_f64`
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.;
| ^^ help: consider adding suffix: `1.0_f64`
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.;
| ^^ help: consider adding suffix: `1.0_f64`
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.;
| ^^ help: consider adding suffix: `1.0_f64`
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.;
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_f64.rs:78:9
--> $DIR/default_numeric_fallback_f64.rs:79:9
|
LL | 1.
| ^^ help: consider adding suffix: `1.0_f64`
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. };
| ^^ help: consider adding suffix: `1.0_f64`
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. };
| ^^ help: consider adding suffix: `1.0_f64`
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.);
| ^^ help: consider adding suffix: `1.0_f64`
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.);
| ^^ help: consider adding suffix: `1.0_f64`
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. };
| ^^ help: consider adding suffix: `1.0_f64`
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. };
| ^^ help: consider adding suffix: `1.0_f64`
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.);
| ^^ help: consider adding suffix: `1.0_f64`
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.);
| ^^ help: consider adding suffix: `1.0_f64`
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.;
| ^^^ help: consider adding suffix: `22.0_f64`

View File

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

View File

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

View File

@ -1,5 +1,5 @@
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;
| ^^ help: consider adding suffix: `22_i32`
@ -7,151 +7,151 @@ LL | let x = 22;
= note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
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];
| ^ help: consider adding suffix: `1_i32`
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];
| ^ help: consider adding suffix: `2_i32`
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];
| ^ help: consider adding suffix: `3_i32`
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) };
| ^ help: consider adding suffix: `1_i32`
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) };
| ^ help: consider adding suffix: `2_i32`
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) };
| ^ help: consider adding suffix: `3_i32`
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) };
| ^ help: consider adding suffix: `4_i32`
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 {
| ^ help: consider adding suffix: `1_i32`
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,
| ^ help: consider adding suffix: `1_i32`
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,
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:26:18
--> $DIR/default_numeric_fallback_i32.rs:27:18
|
LL | _ => 2,
| ^ help: consider adding suffix: `2_i32`
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;
| ^ help: consider adding suffix: `1_i32`
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;
| ^ help: consider adding suffix: `1_i32`
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;
| ^ help: consider adding suffix: `1_i32`
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;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback_i32.rs:79:9
--> $DIR/default_numeric_fallback_i32.rs:80:9
|
LL | 1
| ^ help: consider adding suffix: `1_i32`
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 };
| ^ help: consider adding suffix: `1_i32`
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 };
| ^ help: consider adding suffix: `1_i32`
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);
| ^ help: consider adding suffix: `1_i32`
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);
| ^ help: consider adding suffix: `1_i32`
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 };
| ^ help: consider adding suffix: `1_i32`
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 };
| ^ help: consider adding suffix: `1_i32`
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);
| ^ help: consider adding suffix: `1_i32`
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);
| ^ help: consider adding suffix: `1_i32`
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;
| ^^ 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() {}

View File

@ -267,4 +267,41 @@ fn default() -> Self {
}
}
// 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() {}

View File

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

View File

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

View File

@ -22,9 +22,9 @@ pub fn rustc_lints() {
#[expect(illegal_floating_point_literal_pattern)]
match x {
5.0 => {}
6.0 => {}
_ => {}
5.0 => {},
6.0 => {},
_ => {},
}
}
}
@ -38,9 +38,9 @@ pub fn rustc_lints() {
#[expect(illegal_floating_point_literal_pattern)]
match x {
5 => {}
6 => {}
_ => {}
5 => {},
6 => {},
_ => {},
}
}
}

View File

@ -5,7 +5,7 @@ LL | pub fn a(_: impl Trait) {}
| ^^^^^^^^^^
|
= 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) {}
| +++++++++++++++++++++++++++++++
@ -16,7 +16,7 @@ error: '`impl Trait` used as a function parameter'
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) {}
| +++++++++++++++++++++++++++++++

View File

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

View File

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

View File

@ -282,6 +282,87 @@ pub async fn is_empty(&self) -> bool {
}
}
// 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
pub struct NonStandardLenAndIsEmptySignature;
impl NonStandardLenAndIsEmptySignature {
@ -328,4 +409,15 @@ pub fn len<T, U>(self, resource: &T) -> usize
}
}
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() {}

View File

@ -119,5 +119,23 @@ LL | pub fn len(&self) -> Result<usize, ()> {
|
= 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)]
let _ = f();
}
async fn issue10433() {
let _pending: () = std::future::pending().await;
}

View File

@ -175,3 +175,7 @@ fn f() {}
#[expect(clippy::let_unit_value)]
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
#![warn(clippy::manual_rem_euclid)]
#![allow(clippy::let_with_type_underscore)]
#[macro_use]
extern crate macro_rules;

View File

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

View File

@ -1,5 +1,5 @@
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;
| ^^^^^^^^^^^^^^^^^^^^^ 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`
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;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
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;
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
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;
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
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;
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
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;
| ^^^^^^^^^^^^^^^^^^^^^ 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)
error: manual `rem_euclid` implementation
--> $DIR/manual_rem_euclid.rs:49:5
--> $DIR/manual_rem_euclid.rs:50:5
|
LL | ((num % 4) + 4) % 4
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
error: manual `rem_euclid` implementation
--> $DIR/manual_rem_euclid.rs:54:5
--> $DIR/manual_rem_euclid.rs:55:5
|
LL | ((num % 4) + 4) % 4
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
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;
| ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
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;
| ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`

View File

@ -16,7 +16,7 @@ fn str_to_int_ok(x: &str) -> i32 {
#[rustfmt::skip]
fn strange_some_no_else(x: &str) -> i32 {
{
if let Ok(y) = x . parse() {
if let Ok(y) = x . parse() {
return y;
};
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
|
LL | if let Ok(y) = x . parse() {
LL | if let Ok(y) = x . parse() {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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`
error: missing documentation for a type alias
--> $DIR/missing_doc.rs:17:1
|
LL | pub type PubTypedef = String;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a module
--> $DIR/missing_doc.rs:19:1
|
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
--> $DIR/missing_doc.rs:25:1
|
@ -69,50 +51,18 @@ error: missing documentation for a variant
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
--> $DIR/missing_doc.rs:65:1
|
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
--> $DIR/missing_doc.rs:74:1
|
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
--> $DIR/missing_doc.rs:83:1
|
@ -125,35 +75,17 @@ 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
--> $DIR/missing_doc.rs:88:5
|
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
--> $DIR/missing_doc.rs:94:9
|
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,
| ^^^^^^^^
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
--> $DIR/missing_doc_impl.rs:20:5
|
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
--> $DIR/missing_doc_impl.rs:67:5
|
@ -89,12 +41,6 @@ error: missing documentation for an associated function
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
--> $DIR/missing_doc_impl.rs:78:5
|
@ -103,5 +49,5 @@ LL | | 1
LL | | }
| |_____^
error: aborting due to 15 previous errors
error: aborting due to 7 previous errors

View File

@ -116,4 +116,32 @@ fn issue10259() {
unsafe_macro!();
}
fn _fn_ptr(x: unsafe fn()) {
unsafe {
x();
x();
}
}
fn _assoc_const() {
trait X {
const X: unsafe fn();
}
fn _f<T: X>() {
unsafe {
T::X();
T::X();
}
}
}
fn _field_fn_ptr(x: unsafe fn()) {
struct X(unsafe fn());
let x = X(x);
unsafe {
x.0();
x.0();
}
}
fn main() {}

View File

@ -125,5 +125,65 @@ note: raw pointer dereference occurs here
LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
| ^^^^^^^^^^^^^^^^^^
error: aborting due to 5 previous errors
error: this `unsafe` block contains 2 unsafe operations, expected only one
--> $DIR/multiple_unsafe_ops_per_block.rs:120:5
|
LL | / unsafe {
LL | | x();
LL | | x();
LL | | }
| |_____^
|
note: unsafe function call occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:121:9
|
LL | x();
| ^^^
note: unsafe function call occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:122:9
|
LL | x();
| ^^^
error: this `unsafe` block contains 2 unsafe operations, expected only one
--> $DIR/multiple_unsafe_ops_per_block.rs:131:9
|
LL | / unsafe {
LL | | T::X();
LL | | T::X();
LL | | }
| |_________^
|
note: unsafe function call occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:132:13
|
LL | T::X();
| ^^^^^^
note: unsafe function call occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:133:13
|
LL | T::X();
| ^^^^^^
error: this `unsafe` block contains 2 unsafe operations, expected only one
--> $DIR/multiple_unsafe_ops_per_block.rs:141:5
|
LL | / unsafe {
LL | | x.0();
LL | | x.0();
LL | | }
| |_____^
|
note: unsafe function call occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:142:9
|
LL | x.0();
| ^^^^^
note: unsafe function call occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:143:9
|
LL | x.0();
| ^^^^^
error: aborting due to 8 previous errors

View File

@ -406,7 +406,7 @@ mod issue10041 {
struct Bomb;
impl Bomb {
// Hidden <Rhs = Self> default generic paramter.
// Hidden <Rhs = Self> default generic parameter.
pub fn new() -> impl PartialOrd {
0i32
}

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