mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-27 15:11:30 +00:00
Auto merge of #75405 - flip1995:clippyup, r=Manishearth
Update Clippy Biweekly Clippy update (2 days late, since I wanted to wait for https://github.com/rust-lang/rust/pull/75098) r? @Manishearth
This commit is contained in:
commit
fdc2f879f1
127 changed files with 2700 additions and 619 deletions
|
@ -6,13 +6,13 @@ document.
|
|||
|
||||
## Unreleased / In Rust Nightly
|
||||
|
||||
[c2c07fa...master](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master)
|
||||
[c2c07fa...master](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...master)
|
||||
|
||||
## Rust 1.46
|
||||
|
||||
Current beta, release 2020-08-27
|
||||
|
||||
[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master)
|
||||
[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
|
||||
|
||||
### New lints
|
||||
|
||||
|
@ -1454,6 +1454,7 @@ Released 2018-09-13
|
|||
[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
|
||||
[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
|
||||
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
|
||||
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
|
||||
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
|
||||
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
|
||||
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
|
||||
|
@ -1615,6 +1616,7 @@ Released 2018-09-13
|
|||
[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
|
||||
[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
|
||||
[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
|
||||
[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
|
||||
[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
|
||||
[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
|
||||
[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
|
||||
|
@ -1686,6 +1688,7 @@ Released 2018-09-13
|
|||
[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
|
||||
[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
|
||||
[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
|
||||
[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
|
||||
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
|
||||
[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
|
||||
[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
|
||||
|
@ -1701,6 +1704,7 @@ Released 2018-09-13
|
|||
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
|
||||
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
|
||||
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
|
||||
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
|
||||
[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
|
||||
[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
|
||||
[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
|
||||
|
@ -1723,6 +1727,7 @@ Released 2018-09-13
|
|||
[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
|
||||
[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
|
||||
[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
|
||||
[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
|
||||
[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
|
||||
[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
|
||||
[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
|
||||
|
|
|
@ -28,11 +28,14 @@ All contributors are expected to follow the [Rust Code of Conduct].
|
|||
|
||||
## Getting started
|
||||
|
||||
High level approach:
|
||||
**Note: If this is your first time contributing to Clippy, you should
|
||||
first read the [Basics docs](doc/basics.md).**
|
||||
|
||||
### High level approach
|
||||
|
||||
1. Find something to fix/improve
|
||||
2. Change code (likely some file in `clippy_lints/src/`)
|
||||
3. Follow the instructions in the [docs for writing lints](doc/adding_lints.md) such as running the `setup-toolchain.sh` script
|
||||
3. Follow the instructions in the [Basics docs](doc/basics.md) to get set up
|
||||
4. Run `cargo test` in the root directory and wiggle code until it passes
|
||||
5. Open a PR (also can be done after 2. if you run into problems)
|
||||
|
||||
|
@ -95,16 +98,16 @@ quick read.
|
|||
|
||||
## Getting code-completion for rustc internals to work
|
||||
|
||||
Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals
|
||||
using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not
|
||||
available via a `rustup` component at the time of writing.
|
||||
To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via
|
||||
`git clone https://github.com/rust-lang/rust/`.
|
||||
Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
|
||||
which rust-analyzer will be able to understand.
|
||||
Run `cargo dev ra-setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo
|
||||
you just cloned.
|
||||
The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
|
||||
Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals
|
||||
using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not
|
||||
available via a `rustup` component at the time of writing.
|
||||
To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via
|
||||
`git clone https://github.com/rust-lang/rust/`.
|
||||
Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
|
||||
which rust-analyzer will be able to understand.
|
||||
Run `cargo dev ra-setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo
|
||||
you just cloned.
|
||||
The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
|
||||
Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses.
|
||||
Just make sure to remove the dependencies again before finally making a pull request!
|
||||
|
||||
|
|
|
@ -68,10 +68,11 @@ fn inject_deps_into_manifest(
|
|||
});
|
||||
|
||||
// format a new [dependencies]-block with the new deps we need to inject
|
||||
let mut all_deps = String::from("[dependencies]\n");
|
||||
let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
|
||||
new_deps.for_each(|dep_line| {
|
||||
all_deps.push_str(&dep_line);
|
||||
});
|
||||
all_deps.push_str("\n[dependencies]\n");
|
||||
|
||||
// replace "[dependencies]" with
|
||||
// [dependencies]
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//! checks for attributes
|
||||
|
||||
use crate::reexport::Name;
|
||||
use crate::utils::{
|
||||
first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_help,
|
||||
span_lint_and_sugg, span_lint_and_then, without_block_comments,
|
||||
|
@ -517,7 +516,7 @@ fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>
|
|||
}
|
||||
}
|
||||
|
||||
fn check_attrs(cx: &LateContext<'_>, span: Span, name: Name, attrs: &[Attribute]) {
|
||||
fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
|
||||
if span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
@ -606,7 +605,7 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::as
|
|||
cx,
|
||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
begin_of_attr_to_item,
|
||||
"Found an empty line after an outer attribute. \
|
||||
"found an empty line after an outer attribute. \
|
||||
Perhaps you forgot to add a `!` to make it an inner attribute?",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -82,8 +82,8 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
|
|||
cx,
|
||||
NAIVE_BYTECOUNT,
|
||||
expr.span,
|
||||
"You appear to be counting bytes the naive way",
|
||||
"Consider using the bytecount crate",
|
||||
"you appear to be counting bytes the naive way",
|
||||
"consider using the bytecount crate",
|
||||
format!("bytecount::count({}, {})",
|
||||
snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
|
||||
|
|
|
@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
|||
cx,
|
||||
CHECKED_CONVERSIONS,
|
||||
item.span,
|
||||
"Checked cast can be simplified.",
|
||||
"checked cast can be simplified",
|
||||
"try",
|
||||
format!("{}::try_from({}).is_ok()", to_type, snippet),
|
||||
applicability,
|
||||
|
|
|
@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess {
|
|||
cx,
|
||||
DEFAULT_TRAIT_ACCESS,
|
||||
expr.span,
|
||||
&format!("Calling `{}` is more clear than this expression", replacement),
|
||||
&format!("calling `{}` is more clear than this expression", replacement),
|
||||
"try",
|
||||
replacement,
|
||||
Applicability::Unspecified, // First resolve the TODO above
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::utils::paths;
|
||||
use crate::utils::{
|
||||
is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then,
|
||||
get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help,
|
||||
span_lint_and_note, span_lint_and_then,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::def_id::DefId;
|
||||
|
@ -43,6 +44,57 @@ declare_clippy_lint! {
|
|||
"deriving `Hash` but implementing `PartialEq` explicitly"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for deriving `Ord` but implementing `PartialOrd`
|
||||
/// explicitly or vice versa.
|
||||
///
|
||||
/// **Why is this bad?** The implementation of these traits must agree (for
|
||||
/// example for use with `sort`) so it’s probably a bad idea to use a
|
||||
/// default-generated `Ord` implementation with an explicitly defined
|
||||
/// `PartialOrd`. In particular, the following must hold for any type
|
||||
/// implementing `Ord`:
|
||||
///
|
||||
/// ```text
|
||||
/// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
|
||||
/// ```
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[derive(Ord, PartialEq, Eq)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl PartialOrd for Foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// #[derive(PartialEq, Eq)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl PartialOrd for Foo {
|
||||
/// fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
|
||||
/// Some(self.cmp(other))
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl Ord for Foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
/// or, if you don't need a custom ordering:
|
||||
/// ```rust,ignore
|
||||
/// #[derive(Ord, PartialOrd, PartialEq, Eq)]
|
||||
/// struct Foo;
|
||||
/// ```
|
||||
pub DERIVE_ORD_XOR_PARTIAL_ORD,
|
||||
correctness,
|
||||
"deriving `Ord` but implementing `PartialOrd` explicitly"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for explicit `Clone` implementations for `Copy`
|
||||
/// types.
|
||||
|
@ -103,7 +155,12 @@ declare_clippy_lint! {
|
|||
"deriving `serde::Deserialize` on a type that has methods using `unsafe`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, UNSAFE_DERIVE_DESERIALIZE]);
|
||||
declare_lint_pass!(Derive => [
|
||||
EXPL_IMPL_CLONE_ON_COPY,
|
||||
DERIVE_HASH_XOR_EQ,
|
||||
DERIVE_ORD_XOR_PARTIAL_ORD,
|
||||
UNSAFE_DERIVE_DESERIALIZE
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Derive {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
|
@ -116,6 +173,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
|
|||
let is_automatically_derived = is_automatically_derived(&*item.attrs);
|
||||
|
||||
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
|
||||
check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
|
||||
|
||||
if is_automatically_derived {
|
||||
check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
|
||||
|
@ -180,6 +238,60 @@ fn check_hash_peq<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
|
||||
fn check_ord_partial_ord<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
span: Span,
|
||||
trait_ref: &TraitRef<'_>,
|
||||
ty: Ty<'tcx>,
|
||||
ord_is_automatically_derived: bool,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(ord_trait_def_id) = get_trait_def_id(cx, &paths::ORD);
|
||||
if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait();
|
||||
if let Some(def_id) = &trait_ref.trait_def_id();
|
||||
if *def_id == ord_trait_def_id;
|
||||
then {
|
||||
// Look for the PartialOrd implementations for `ty`
|
||||
cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
|
||||
let partial_ord_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id));
|
||||
|
||||
if partial_ord_is_automatically_derived == ord_is_automatically_derived {
|
||||
return;
|
||||
}
|
||||
|
||||
let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
|
||||
|
||||
// Only care about `impl PartialOrd<Foo> for Foo`
|
||||
// For `impl PartialOrd<B> for A, input_types is [A, B]
|
||||
if trait_ref.substs.type_at(1) == ty {
|
||||
let mess = if partial_ord_is_automatically_derived {
|
||||
"you are implementing `Ord` explicitly but have derived `PartialOrd`"
|
||||
} else {
|
||||
"you are deriving `Ord` but have implemented `PartialOrd` explicitly"
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DERIVE_ORD_XOR_PARTIAL_ORD,
|
||||
span,
|
||||
mess,
|
||||
|diag| {
|
||||
if let Some(local_def_id) = impl_id.as_local() {
|
||||
let hir_id = cx.tcx.hir().as_local_hir_id(local_def_id);
|
||||
diag.span_note(
|
||||
cx.tcx.hir().span(hir_id),
|
||||
"`PartialOrd` implemented here"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
|
||||
fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
|
||||
if match_path(&trait_ref.path, &paths::CLONE_TRAIT) {
|
||||
|
@ -242,7 +354,9 @@ fn check_unsafe_derive_deserialize<'tcx>(
|
|||
if_chain! {
|
||||
if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE);
|
||||
if let ty::Adt(def, _) = ty.kind;
|
||||
if def.did.is_local();
|
||||
if let Some(local_def_id) = def.did.as_local();
|
||||
let adt_hir_id = cx.tcx.hir().as_local_hir_id(local_def_id);
|
||||
if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
|
||||
if cx.tcx.inherent_impls(def.did)
|
||||
.iter()
|
||||
.map(|imp_did| item_from_def_id(cx, *imp_did))
|
||||
|
|
|
@ -60,7 +60,7 @@ impl<'tcx> DoubleComparisons {
|
|||
cx,
|
||||
DOUBLE_COMPARISONS,
|
||||
span,
|
||||
"This binary expression can be simplified",
|
||||
"this binary expression can be simplified",
|
||||
"try",
|
||||
sugg,
|
||||
applicability,
|
||||
|
|
|
@ -45,15 +45,12 @@ impl EarlyLintPass for DoubleParens {
|
|||
return;
|
||||
}
|
||||
|
||||
let msg: &str = "consider removing unnecessary double parentheses";
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::Paren(ref in_paren) => match in_paren.kind {
|
||||
ExprKind::Paren(_) | ExprKind::Tup(_) => {
|
||||
span_lint(
|
||||
cx,
|
||||
DOUBLE_PARENS,
|
||||
expr.span,
|
||||
"Consider removing unnecessary double parentheses",
|
||||
);
|
||||
span_lint(cx, DOUBLE_PARENS, expr.span, &msg);
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
|
@ -61,12 +58,7 @@ impl EarlyLintPass for DoubleParens {
|
|||
if params.len() == 1 {
|
||||
let param = ¶ms[0];
|
||||
if let ExprKind::Paren(_) = param.kind {
|
||||
span_lint(
|
||||
cx,
|
||||
DOUBLE_PARENS,
|
||||
param.span,
|
||||
"Consider removing unnecessary double parentheses",
|
||||
);
|
||||
span_lint(cx, DOUBLE_PARENS, param.span, &msg);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -74,12 +66,7 @@ impl EarlyLintPass for DoubleParens {
|
|||
if params.len() == 2 {
|
||||
let param = ¶ms[1];
|
||||
if let ExprKind::Paren(_) = param.kind {
|
||||
span_lint(
|
||||
cx,
|
||||
DOUBLE_PARENS,
|
||||
param.span,
|
||||
"Consider removing unnecessary double parentheses",
|
||||
);
|
||||
span_lint(cx, DOUBLE_PARENS, param.span, &msg);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -33,11 +33,11 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
pub DROP_BOUNDS,
|
||||
correctness,
|
||||
"Bounds of the form `T: Drop` are useless"
|
||||
"bounds of the form `T: Drop` are useless"
|
||||
}
|
||||
|
||||
const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \
|
||||
Use `std::mem::needs_drop` to detect if a type has drop glue.";
|
||||
const DROP_BOUNDS_SUMMARY: &str = "bounds of the form `T: Drop` are useless, \
|
||||
use `std::mem::needs_drop` to detect if a type has drop glue";
|
||||
|
||||
declare_lint_pass!(DropBounds => [DROP_BOUNDS]);
|
||||
|
||||
|
|
|
@ -294,7 +294,8 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
|
|||
let body = cx.tcx.hir().body(eid);
|
||||
Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id);
|
||||
|
||||
if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) {
|
||||
if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs)
|
||||
{
|
||||
check_must_use_candidate(
|
||||
cx,
|
||||
&sig.decl,
|
||||
|
@ -373,7 +374,7 @@ impl<'tcx> Functions {
|
|||
}
|
||||
|
||||
if line_count > self.max_lines {
|
||||
span_lint(cx, TOO_MANY_LINES, span, "This function has a large number of lines.")
|
||||
span_lint(cx, TOO_MANY_LINES, span, "this function has a large number of lines")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -250,6 +250,7 @@ mod mut_mut;
|
|||
mod mut_reference;
|
||||
mod mutable_debug_assertion;
|
||||
mod mutex_atomic;
|
||||
mod needless_arbitrary_self_type;
|
||||
mod needless_bool;
|
||||
mod needless_borrow;
|
||||
mod needless_borrowed_ref;
|
||||
|
@ -288,6 +289,7 @@ mod serde_api;
|
|||
mod shadow;
|
||||
mod single_component_path_imports;
|
||||
mod slow_vector_initialization;
|
||||
mod stable_sort_primitive;
|
||||
mod strings;
|
||||
mod suspicious_trait_impl;
|
||||
mod swap;
|
||||
|
@ -322,10 +324,6 @@ mod zero_div_zero;
|
|||
|
||||
pub use crate::utils::conf::Conf;
|
||||
|
||||
mod reexport {
|
||||
pub use rustc_span::Symbol as Name;
|
||||
}
|
||||
|
||||
/// Register all pre expansion lints
|
||||
///
|
||||
/// Pre-expansion lints run before any macro expansion has happened.
|
||||
|
@ -513,6 +511,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&default_trait_access::DEFAULT_TRAIT_ACCESS,
|
||||
&dereference::EXPLICIT_DEREF_METHODS,
|
||||
&derive::DERIVE_HASH_XOR_EQ,
|
||||
&derive::DERIVE_ORD_XOR_PARTIAL_ORD,
|
||||
&derive::EXPL_IMPL_CLONE_ON_COPY,
|
||||
&derive::UNSAFE_DERIVE_DESERIALIZE,
|
||||
&doc::DOC_MARKDOWN,
|
||||
|
@ -610,6 +609,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&loops::NEEDLESS_COLLECT,
|
||||
&loops::NEEDLESS_RANGE_LOOP,
|
||||
&loops::NEVER_LOOP,
|
||||
&loops::SAME_ITEM_PUSH,
|
||||
&loops::WHILE_IMMUTABLE_CONDITION,
|
||||
&loops::WHILE_LET_LOOP,
|
||||
&loops::WHILE_LET_ON_ITERATOR,
|
||||
|
@ -719,6 +719,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
|
||||
&mutex_atomic::MUTEX_ATOMIC,
|
||||
&mutex_atomic::MUTEX_INTEGER,
|
||||
&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
|
||||
&needless_bool::BOOL_COMPARISON,
|
||||
&needless_bool::NEEDLESS_BOOL,
|
||||
&needless_borrow::NEEDLESS_BORROW,
|
||||
|
@ -776,6 +777,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&shadow::SHADOW_UNRELATED,
|
||||
&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
|
||||
&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
|
||||
&stable_sort_primitive::STABLE_SORT_PRIMITIVE,
|
||||
&strings::STRING_ADD,
|
||||
&strings::STRING_ADD_ASSIGN,
|
||||
&strings::STRING_LIT_AS_BYTES,
|
||||
|
@ -786,6 +788,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
|
||||
&temporary_assignment::TEMPORARY_ASSIGNMENT,
|
||||
&to_digit_is_some::TO_DIGIT_IS_SOME,
|
||||
&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
|
||||
&trait_bounds::TYPE_REPETITION_IN_BOUNDS,
|
||||
&transmute::CROSSPOINTER_TRANSMUTE,
|
||||
&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
||||
|
@ -1028,6 +1031,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
|
||||
store.register_early_pass(|| box precedence::Precedence);
|
||||
store.register_early_pass(|| box needless_continue::NeedlessContinue);
|
||||
store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType);
|
||||
store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes);
|
||||
store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
|
||||
store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
|
||||
|
@ -1079,6 +1083,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box macro_use::MacroUseImports::default());
|
||||
store.register_late_pass(|| box map_identity::MapIdentity);
|
||||
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
|
||||
store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
|
||||
store.register_late_pass(|| box repeat_once::RepeatOnce);
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
|
@ -1175,6 +1180,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&ranges::RANGE_PLUS_ONE),
|
||||
LintId::of(&shadow::SHADOW_UNRELATED),
|
||||
LintId::of(&strings::STRING_ADD_ASSIGN),
|
||||
LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
|
||||
LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
|
||||
LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF),
|
||||
LintId::of(&types::CAST_LOSSLESS),
|
||||
|
@ -1231,6 +1237,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&copies::IFS_SAME_COND),
|
||||
LintId::of(&copies::IF_SAME_THEN_ELSE),
|
||||
LintId::of(&derive::DERIVE_HASH_XOR_EQ),
|
||||
LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD),
|
||||
LintId::of(&doc::MISSING_SAFETY_DOC),
|
||||
LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),
|
||||
LintId::of(&double_comparison::DOUBLE_COMPARISONS),
|
||||
|
@ -1293,6 +1300,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&loops::NEEDLESS_COLLECT),
|
||||
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
|
||||
LintId::of(&loops::NEVER_LOOP),
|
||||
LintId::of(&loops::SAME_ITEM_PUSH),
|
||||
LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
|
||||
LintId::of(&loops::WHILE_LET_LOOP),
|
||||
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
|
||||
|
@ -1369,6 +1377,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&mut_key::MUTABLE_KEY_TYPE),
|
||||
LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
|
||||
LintId::of(&mutex_atomic::MUTEX_ATOMIC),
|
||||
LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
|
||||
LintId::of(&needless_bool::BOOL_COMPARISON),
|
||||
LintId::of(&needless_bool::NEEDLESS_BOOL),
|
||||
LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
|
||||
|
@ -1409,6 +1418,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&serde_api::SERDE_API_MISUSE),
|
||||
LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
||||
LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
|
||||
LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(&strings::STRING_LIT_AS_BYTES),
|
||||
LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||
LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
|
@ -1495,6 +1505,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&loops::EMPTY_LOOP),
|
||||
LintId::of(&loops::FOR_KV_MAP),
|
||||
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
|
||||
LintId::of(&loops::SAME_ITEM_PUSH),
|
||||
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
|
||||
LintId::of(&main_recursion::MAIN_RECURSION),
|
||||
LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
|
||||
|
@ -1600,6 +1611,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&misc::SHORT_CIRCUIT_STATEMENT),
|
||||
LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN),
|
||||
LintId::of(&misc_early::ZERO_PREFIXED_LITERAL),
|
||||
LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
|
||||
LintId::of(&needless_bool::BOOL_COMPARISON),
|
||||
LintId::of(&needless_bool::NEEDLESS_BOOL),
|
||||
LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
|
||||
|
@ -1651,6 +1663,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&copies::IFS_SAME_COND),
|
||||
LintId::of(&copies::IF_SAME_THEN_ELSE),
|
||||
LintId::of(&derive::DERIVE_HASH_XOR_EQ),
|
||||
LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD),
|
||||
LintId::of(&drop_bounds::DROP_BOUNDS),
|
||||
LintId::of(&drop_forget_ref::DROP_COPY),
|
||||
LintId::of(&drop_forget_ref::DROP_REF),
|
||||
|
@ -1726,6 +1739,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&mutex_atomic::MUTEX_ATOMIC),
|
||||
LintId::of(&redundant_clone::REDUNDANT_CLONE),
|
||||
LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
|
||||
LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(&types::BOX_VEC),
|
||||
LintId::of(&types::REDUNDANT_ALLOCATION),
|
||||
LintId::of(&vec::USELESS_VEC),
|
||||
|
|
|
@ -13,9 +13,8 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::symbol::{kw, Symbol};
|
||||
|
||||
use crate::reexport::Name;
|
||||
use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -113,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
|||
enum RefLt {
|
||||
Unnamed,
|
||||
Static,
|
||||
Named(Name),
|
||||
Named(Symbol),
|
||||
}
|
||||
|
||||
fn check_fn_inner<'tcx>(
|
||||
|
@ -456,7 +455,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
|
|||
}
|
||||
|
||||
struct LifetimeChecker {
|
||||
map: FxHashMap<Name, Span>,
|
||||
map: FxHashMap<Symbol, Span>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for LifetimeChecker {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use crate::consts::constant;
|
||||
use crate::reexport::Name;
|
||||
use crate::utils::paths;
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::usage::{is_unused, mutated_variables};
|
||||
use crate::utils::{
|
||||
get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
|
||||
is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var,
|
||||
multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help,
|
||||
span_lint_and_sugg, span_lint_and_then, SpanlessEq,
|
||||
is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
|
||||
match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability,
|
||||
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
|
||||
SpanlessEq,
|
||||
};
|
||||
use crate::utils::{is_type_diagnostic_item, qpath_res, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
|
@ -17,7 +17,7 @@ use rustc_hir::def::{DefKind, Res};
|
|||
use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{
|
||||
def_id, BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, InlineAsmOperand,
|
||||
LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind,
|
||||
Local, LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind,
|
||||
};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
@ -27,7 +27,7 @@ use rustc_middle::middle::region;
|
|||
use rustc_middle::ty::{self, Ty, TyS};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
|
||||
use std::iter::{once, Iterator};
|
||||
use std::mem;
|
||||
|
@ -420,6 +420,39 @@ declare_clippy_lint! {
|
|||
"variables used within while expression are not mutated in the body"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks whether a for loop is being used to push a constant
|
||||
/// value into a Vec.
|
||||
///
|
||||
/// **Why is this bad?** This kind of operation can be expressed more succinctly with
|
||||
/// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also
|
||||
/// have better performance.
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let item1 = 2;
|
||||
/// let item2 = 3;
|
||||
/// let mut vec: Vec<u8> = Vec::new();
|
||||
/// for _ in 0..20 {
|
||||
/// vec.push(item1);
|
||||
/// }
|
||||
/// for _ in 0..30 {
|
||||
/// vec.push(item2);
|
||||
/// }
|
||||
/// ```
|
||||
/// could be written as
|
||||
/// ```rust
|
||||
/// let item1 = 2;
|
||||
/// let item2 = 3;
|
||||
/// let mut vec: Vec<u8> = vec![item1; 20];
|
||||
/// vec.resize(20 + 30, item2);
|
||||
/// ```
|
||||
pub SAME_ITEM_PUSH,
|
||||
style,
|
||||
"the same item is pushed inside of a for loop"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Loops => [
|
||||
MANUAL_MEMCPY,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
|
@ -436,6 +469,7 @@ declare_lint_pass!(Loops => [
|
|||
NEVER_LOOP,
|
||||
MUT_RANGE_BOUND,
|
||||
WHILE_IMMUTABLE_CONDITION,
|
||||
SAME_ITEM_PUSH,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Loops {
|
||||
|
@ -741,6 +775,7 @@ fn check_for_loop<'tcx>(
|
|||
check_for_loop_over_map_kv(cx, pat, arg, body, expr);
|
||||
check_for_mut_range_bound(cx, arg, body);
|
||||
detect_manual_memcpy(cx, pat, arg, body, expr);
|
||||
detect_same_item_push(cx, pat, arg, body, expr);
|
||||
}
|
||||
|
||||
fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool {
|
||||
|
@ -1017,6 +1052,117 @@ fn detect_manual_memcpy<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
// Scans the body of the for loop and determines whether lint should be given
|
||||
struct SameItemPushVisitor<'a, 'tcx> {
|
||||
should_lint: bool,
|
||||
// this field holds the last vec push operation visited, which should be the only push seen
|
||||
vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
match &expr.kind {
|
||||
// Non-determinism may occur ... don't give a lint
|
||||
ExprKind::Loop(_, _, _) | ExprKind::Match(_, _, _) => self.should_lint = false,
|
||||
ExprKind::Block(block, _) => self.visit_block(block),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, b: &'tcx Block<'_>) {
|
||||
for stmt in b.stmts.iter() {
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, s: &'tcx Stmt<'_>) {
|
||||
let vec_push_option = get_vec_push(self.cx, s);
|
||||
if vec_push_option.is_none() {
|
||||
// Current statement is not a push so visit inside
|
||||
match &s.kind {
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(&expr),
|
||||
_ => {},
|
||||
}
|
||||
} else {
|
||||
// Current statement is a push ...check whether another
|
||||
// push had been previously done
|
||||
if self.vec_push.is_none() {
|
||||
self.vec_push = vec_push_option;
|
||||
} else {
|
||||
// There are multiple pushes ... don't lint
|
||||
self.should_lint = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
// Given some statement, determine if that statement is a push on a Vec. If it is, return
|
||||
// the Vec being pushed into and the item being pushed
|
||||
fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
|
||||
if_chain! {
|
||||
// Extract method being called
|
||||
if let StmtKind::Semi(semi_stmt) = &stmt.kind;
|
||||
if let ExprKind::MethodCall(path, _, args, _) = &semi_stmt.kind;
|
||||
// Figure out the parameters for the method call
|
||||
if let Some(self_expr) = args.get(0);
|
||||
if let Some(pushed_item) = args.get(1);
|
||||
// Check that the method being called is push() on a Vec
|
||||
if match_type(cx, cx.typeck_results().expr_ty(self_expr), &paths::VEC);
|
||||
if path.ident.name.as_str() == "push";
|
||||
then {
|
||||
return Some((self_expr, pushed_item))
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Detects for loop pushing the same item into a Vec
|
||||
fn detect_same_item_push<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
pat: &'tcx Pat<'_>,
|
||||
_: &'tcx Expr<'_>,
|
||||
body: &'tcx Expr<'_>,
|
||||
_: &'tcx Expr<'_>,
|
||||
) {
|
||||
// Determine whether it is safe to lint the body
|
||||
let mut same_item_push_visitor = SameItemPushVisitor {
|
||||
should_lint: true,
|
||||
vec_push: None,
|
||||
cx,
|
||||
};
|
||||
walk_expr(&mut same_item_push_visitor, body);
|
||||
if same_item_push_visitor.should_lint {
|
||||
if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push {
|
||||
// Make sure that the push does not involve possibly mutating values
|
||||
if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
|
||||
if let PatKind::Wild = pat.kind {
|
||||
let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
|
||||
let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
|
||||
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
SAME_ITEM_PUSH,
|
||||
vec.span,
|
||||
"it looks like the same item is being pushed into this Vec",
|
||||
None,
|
||||
&format!(
|
||||
"try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
|
||||
item_str, vec_str, item_str
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks for looping over a range and then indexing a sequence with it.
|
||||
/// The iteratee must be a range literal.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
|
@ -1184,7 +1330,7 @@ fn check_for_loop_range<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn is_len_call(expr: &Expr<'_>, var: Name) -> bool {
|
||||
fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(ref method, _, ref len_args, _) = expr.kind;
|
||||
if len_args.len() == 1;
|
||||
|
@ -1640,15 +1786,15 @@ struct VarVisitor<'a, 'tcx> {
|
|||
/// var name to look for as index
|
||||
var: HirId,
|
||||
/// indexed variables that are used mutably
|
||||
indexed_mut: FxHashSet<Name>,
|
||||
indexed_mut: FxHashSet<Symbol>,
|
||||
/// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
|
||||
indexed_indirectly: FxHashMap<Name, Option<region::Scope>>,
|
||||
indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>,
|
||||
/// subset of `indexed` of vars that are indexed directly: `v[i]`
|
||||
/// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
|
||||
indexed_directly: FxHashMap<Name, (Option<region::Scope>, Ty<'tcx>)>,
|
||||
indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
|
||||
/// Any names that are used outside an index operation.
|
||||
/// Used to detect things like `&mut vec` used together with `vec[i]`
|
||||
referenced: FxHashSet<Name>,
|
||||
referenced: FxHashSet<Symbol>,
|
||||
/// has the loop variable been used in expressions other than the index of
|
||||
/// an index op?
|
||||
nonindex: bool,
|
||||
|
@ -2004,7 +2150,7 @@ struct InitializeVisitor<'a, 'tcx> {
|
|||
end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here.
|
||||
var_id: HirId,
|
||||
state: VarState,
|
||||
name: Option<Name>,
|
||||
name: Option<Symbol>,
|
||||
depth: u32, // depth of conditional expressions
|
||||
past_loop: bool,
|
||||
}
|
||||
|
@ -2167,7 +2313,7 @@ use self::Nesting::{LookFurther, RuledOut, Unknown};
|
|||
|
||||
struct LoopNestVisitor {
|
||||
hir_id: HirId,
|
||||
iterator: Name,
|
||||
iterator: Symbol,
|
||||
nesting: Nesting,
|
||||
}
|
||||
|
||||
|
@ -2218,7 +2364,7 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
fn path_name(e: &Expr<'_>) -> Option<Name> {
|
||||
fn path_name(e: &Expr<'_>) -> Option<Symbol> {
|
||||
if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind {
|
||||
let segments = &path.segments;
|
||||
if segments.len() == 1 {
|
||||
|
@ -2358,6 +2504,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> {
|
|||
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
|
||||
|
||||
fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
|
||||
check_needless_collect_direct_usage(expr, cx);
|
||||
check_needless_collect_indirect_usage(expr, cx);
|
||||
}
|
||||
fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
|
||||
if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind;
|
||||
|
@ -2371,7 +2521,7 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
|
|||
match_type(cx, ty, &paths::BTREEMAP) ||
|
||||
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {
|
||||
if method.ident.name == sym!(len) {
|
||||
let span = shorten_span(expr, sym!(collect));
|
||||
let span = shorten_needless_collect_span(expr);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
|
@ -2383,20 +2533,20 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
|
|||
);
|
||||
}
|
||||
if method.ident.name == sym!(is_empty) {
|
||||
let span = shorten_span(expr, sym!(iter));
|
||||
let span = shorten_needless_collect_span(expr);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
span,
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
"replace with",
|
||||
"get(0).is_none()".to_string(),
|
||||
"next().is_none()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
if method.ident.name == sym!(contains) {
|
||||
let contains_arg = snippet(cx, args[1].span, "??");
|
||||
let span = shorten_span(expr, sym!(collect));
|
||||
let span = shorten_needless_collect_span(expr);
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
|
@ -2425,13 +2575,164 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
|
|||
}
|
||||
}
|
||||
|
||||
fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span {
|
||||
let mut current_expr = expr;
|
||||
while let ExprKind::MethodCall(ref path, ref span, ref args, _) = current_expr.kind {
|
||||
if path.ident.name == target_fn_name {
|
||||
fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
|
||||
if let ExprKind::Block(ref block, _) = expr.kind {
|
||||
for ref stmt in block.stmts {
|
||||
if_chain! {
|
||||
if let StmtKind::Local(
|
||||
Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. },
|
||||
init: Some(ref init_expr), .. }
|
||||
) = stmt.kind;
|
||||
if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind;
|
||||
if method_name.ident.name == sym!(collect) && match_trait_method(cx, &init_expr, &paths::ITERATOR);
|
||||
if let Some(ref generic_args) = method_name.args;
|
||||
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
|
||||
if let ty = cx.typeck_results().node_type(ty.hir_id);
|
||||
if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
|
||||
is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
|
||||
match_type(cx, ty, &paths::LINKED_LIST);
|
||||
if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
|
||||
if iter_calls.len() == 1;
|
||||
then {
|
||||
// Suggest replacing iter_call with iter_replacement, and removing stmt
|
||||
let iter_call = &iter_calls[0];
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
stmt.span.until(iter_call.span),
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
|diag| {
|
||||
let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx));
|
||||
diag.multipart_suggestion(
|
||||
iter_call.get_suggestion_text(),
|
||||
vec![
|
||||
(stmt.span, String::new()),
|
||||
(iter_call.span, iter_replacement)
|
||||
],
|
||||
Applicability::MachineApplicable,// MaybeIncorrect,
|
||||
).emit();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct IterFunction {
|
||||
func: IterFunctionKind,
|
||||
span: Span,
|
||||
}
|
||||
impl IterFunction {
|
||||
fn get_iter_method(&self, cx: &LateContext<'_>) -> String {
|
||||
match &self.func {
|
||||
IterFunctionKind::IntoIter => String::new(),
|
||||
IterFunctionKind::Len => String::from(".count()"),
|
||||
IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
|
||||
IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")),
|
||||
}
|
||||
}
|
||||
fn get_suggestion_text(&self) -> &'static str {
|
||||
match &self.func {
|
||||
IterFunctionKind::IntoIter => {
|
||||
"Use the original Iterator instead of collecting it and then producing a new one"
|
||||
},
|
||||
IterFunctionKind::Len => {
|
||||
"Take the original Iterator's count instead of collecting it and finding the length"
|
||||
},
|
||||
IterFunctionKind::IsEmpty => {
|
||||
"Check if the original Iterator has anything instead of collecting it and seeing if it's empty"
|
||||
},
|
||||
IterFunctionKind::Contains(_) => {
|
||||
"Check if the original Iterator contains an element instead of collecting then checking"
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
enum IterFunctionKind {
|
||||
IntoIter,
|
||||
Len,
|
||||
IsEmpty,
|
||||
Contains(Span),
|
||||
}
|
||||
|
||||
struct IterFunctionVisitor {
|
||||
uses: Vec<IterFunction>,
|
||||
seen_other: bool,
|
||||
target: Ident,
|
||||
}
|
||||
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
// Check function calls on our collection
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind;
|
||||
if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0);
|
||||
if let &[name] = &path.segments;
|
||||
if name.ident == self.target;
|
||||
then {
|
||||
let len = sym!(len);
|
||||
let is_empty = sym!(is_empty);
|
||||
let contains = sym!(contains);
|
||||
match method_name.ident.name {
|
||||
sym::into_iter => self.uses.push(
|
||||
IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }
|
||||
),
|
||||
name if name == len => self.uses.push(
|
||||
IterFunction { func: IterFunctionKind::Len, span: expr.span }
|
||||
),
|
||||
name if name == is_empty => self.uses.push(
|
||||
IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }
|
||||
),
|
||||
name if name == contains => self.uses.push(
|
||||
IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }
|
||||
),
|
||||
_ => self.seen_other = true,
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
// Check if the collection is used for anything else
|
||||
if_chain! {
|
||||
if let Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. } = expr;
|
||||
if let &[name] = &path.segments;
|
||||
if name.ident == self.target;
|
||||
then {
|
||||
self.seen_other = true;
|
||||
} else {
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Map = Map<'tcx>;
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect the occurences of calls to `iter` or `into_iter` for the
|
||||
/// given identifier
|
||||
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
|
||||
let mut visitor = IterFunctionVisitor {
|
||||
uses: Vec::new(),
|
||||
target: identifier,
|
||||
seen_other: false,
|
||||
};
|
||||
visitor.visit_block(block);
|
||||
if visitor.seen_other {
|
||||
None
|
||||
} else {
|
||||
Some(visitor.uses)
|
||||
}
|
||||
}
|
||||
|
||||
fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(.., args, _) = &expr.kind;
|
||||
if let ExprKind::MethodCall(_, span, ..) = &args[0].kind;
|
||||
then {
|
||||
return expr.span.with_lo(span.lo());
|
||||
}
|
||||
current_expr = &args[0];
|
||||
}
|
||||
unreachable!()
|
||||
unreachable!();
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ use if_chain::if_chain;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync,
|
||||
ItemKind, TraitRef, Ty, TyKind, TypeBindingKind,
|
||||
AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId,
|
||||
IsAsync, ItemKind, LifetimeName, TraitRef, Ty, TyKind, TypeBindingKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -27,8 +27,6 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::future::Future;
|
||||
///
|
||||
/// async fn foo() -> i32 { 42 }
|
||||
/// ```
|
||||
pub MANUAL_ASYNC_FN,
|
||||
|
@ -53,8 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
if let IsAsync::NotAsync = header.asyncness;
|
||||
// Check that this function returns `impl Future`
|
||||
if let FnRetTy::Return(ret_ty) = decl.output;
|
||||
if let Some(trait_ref) = future_trait_ref(cx, ret_ty);
|
||||
if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty);
|
||||
if let Some(output) = future_output_ty(trait_ref);
|
||||
if captures_all_lifetimes(decl.inputs, &output_lifetimes);
|
||||
// Check that the body of the function consists of one async block
|
||||
if let ExprKind::Block(block, _) = body.value.kind;
|
||||
if block.stmts.is_empty();
|
||||
|
@ -97,16 +96,35 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
}
|
||||
}
|
||||
|
||||
fn future_trait_ref<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> {
|
||||
fn future_trait_ref<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ty: &'tcx Ty<'tcx>,
|
||||
) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> {
|
||||
if_chain! {
|
||||
if let TyKind::OpaqueDef(item_id, _) = ty.kind;
|
||||
if let TyKind::OpaqueDef(item_id, bounds) = ty.kind;
|
||||
let item = cx.tcx.hir().item(item_id.id);
|
||||
if let ItemKind::OpaqueTy(opaque) = &item.kind;
|
||||
if opaque.bounds.len() == 1;
|
||||
if let GenericBound::Trait(poly, _) = &opaque.bounds[0];
|
||||
if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
|
||||
if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
|
||||
if let GenericBound::Trait(poly, _) = bound {
|
||||
Some(&poly.trait_ref)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
|
||||
then {
|
||||
return Some(&poly.trait_ref);
|
||||
let output_lifetimes = bounds
|
||||
.iter()
|
||||
.filter_map(|bound| {
|
||||
if let GenericArg::Lifetime(lt) = bound {
|
||||
Some(lt.name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
return Some((trait_ref, output_lifetimes));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,6 +147,29 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t
|
|||
None
|
||||
}
|
||||
|
||||
fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) -> bool {
|
||||
let input_lifetimes: Vec<LifetimeName> = inputs
|
||||
.iter()
|
||||
.filter_map(|ty| {
|
||||
if let TyKind::Rptr(lt, _) = ty.kind {
|
||||
Some(lt.name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// The lint should trigger in one of these cases:
|
||||
// - There are no input lifetimes
|
||||
// - There's only one output lifetime bound using `+ '_`
|
||||
// - All input lifetimes are explicitly bound to the output
|
||||
input_lifetimes.is_empty()
|
||||
|| (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore))
|
||||
|| input_lifetimes
|
||||
.iter()
|
||||
.all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt))
|
||||
}
|
||||
|
||||
fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
|
||||
if_chain! {
|
||||
if let Some(block_expr) = block.expr;
|
||||
|
|
|
@ -1052,8 +1052,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Why is this bad?** Readability.
|
||||
///
|
||||
/// **Known problems:** False positive in pattern guards. Will be resolved once
|
||||
/// non-lexical lifetimes are stable.
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
|
@ -1408,7 +1407,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true),
|
||||
["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]),
|
||||
["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]),
|
||||
["next", "skip"] => lint_iter_skip_next(cx, expr),
|
||||
["next", "skip"] => lint_iter_skip_next(cx, expr, arg_lists[1]),
|
||||
["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]),
|
||||
["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]),
|
||||
["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]),
|
||||
|
@ -2433,17 +2432,21 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args:
|
|||
);
|
||||
}
|
||||
|
||||
fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
|
||||
fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) {
|
||||
// lint if caller of skip is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
ITER_SKIP_NEXT,
|
||||
expr.span,
|
||||
"called `skip(x).next()` on an iterator",
|
||||
None,
|
||||
"this is more succinctly expressed by calling `nth(x)`",
|
||||
);
|
||||
if let [caller, n] = skip_args {
|
||||
let hint = format!(".nth({})", snippet(cx, n.span, ".."));
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_SKIP_NEXT,
|
||||
expr.span.trim_start(caller.span).unwrap(),
|
||||
"called `skip(x).next()` on an iterator",
|
||||
"use `nth` instead",
|
||||
hint,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2565,17 +2568,34 @@ fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Ex
|
|||
fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) {
|
||||
// lint if caller of `.map().flatten()` is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `map(..).flatten()` on an `Iterator`. \
|
||||
This is more succinctly expressed by calling `.flat_map(..)`";
|
||||
let self_snippet = snippet(cx, map_args[0].span, "..");
|
||||
let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]);
|
||||
let is_map_to_option = match map_closure_ty.kind {
|
||||
ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
|
||||
let map_closure_sig = match map_closure_ty.kind {
|
||||
ty::Closure(_, substs) => substs.as_closure().sig(),
|
||||
_ => map_closure_ty.fn_sig(cx.tcx),
|
||||
};
|
||||
let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&map_closure_sig.output());
|
||||
is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type))
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let method_to_use = if is_map_to_option {
|
||||
// `(...).map(...)` has type `impl Iterator<Item=Option<...>>
|
||||
"filter_map"
|
||||
} else {
|
||||
// `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
|
||||
"flat_map"
|
||||
};
|
||||
let func_snippet = snippet(cx, map_args[1].span, "..");
|
||||
let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet);
|
||||
let hint = format!(".{0}({1})", method_to_use, func_snippet);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_FLATTEN,
|
||||
expr.span,
|
||||
msg,
|
||||
"try using `flat_map` instead",
|
||||
expr.span.with_lo(map_args[0].span.hi()),
|
||||
"called `map(..).flatten()` on an `Iterator`",
|
||||
&format!("try using `{}` instead", method_to_use),
|
||||
hint,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
@ -2583,16 +2603,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
|
|||
|
||||
// lint if caller of `.map().flatten()` is an Option
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) {
|
||||
let msg = "called `map(..).flatten()` on an `Option`. \
|
||||
This is more succinctly expressed by calling `.and_then(..)`";
|
||||
let self_snippet = snippet(cx, map_args[0].span, "..");
|
||||
let func_snippet = snippet(cx, map_args[1].span, "..");
|
||||
let hint = format!("{0}.and_then({1})", self_snippet, func_snippet);
|
||||
let hint = format!(".and_then({})", func_snippet);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_FLATTEN,
|
||||
expr.span,
|
||||
msg,
|
||||
expr.span.with_lo(map_args[0].span.hi()),
|
||||
"called `map(..).flatten()` on an `Option`",
|
||||
"try using `and_then` instead",
|
||||
hint,
|
||||
Applicability::MachineApplicable,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::consts::{constant_simple, Constant};
|
||||
use crate::utils::{match_def_path, paths, span_lint};
|
||||
use crate::utils::{match_def_path, match_trait_method, paths, span_lint};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -18,6 +19,10 @@ declare_clippy_lint! {
|
|||
/// ```ignore
|
||||
/// min(0, max(100, x))
|
||||
/// ```
|
||||
/// or
|
||||
/// ```ignore
|
||||
/// x.max(100).min(0)
|
||||
/// ```
|
||||
/// It will always be equal to `0`. Probably the author meant to clamp the value
|
||||
/// between 0 and 100, but has erroneously swapped `min` and `max`.
|
||||
pub MIN_MAX,
|
||||
|
@ -60,25 +65,43 @@ enum MinMax {
|
|||
}
|
||||
|
||||
fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
|
||||
if let ExprKind::Call(ref path, ref args) = expr.kind {
|
||||
if let ExprKind::Path(ref qpath) = path.kind {
|
||||
cx.typeck_results()
|
||||
.qpath_res(qpath, path.hir_id)
|
||||
.opt_def_id()
|
||||
.and_then(|def_id| {
|
||||
if match_def_path(cx, def_id, &paths::CMP_MIN) {
|
||||
fetch_const(cx, args, MinMax::Min)
|
||||
} else if match_def_path(cx, def_id, &paths::CMP_MAX) {
|
||||
match expr.kind {
|
||||
ExprKind::Call(ref path, ref args) => {
|
||||
if let ExprKind::Path(ref qpath) = path.kind {
|
||||
cx.typeck_results()
|
||||
.qpath_res(qpath, path.hir_id)
|
||||
.opt_def_id()
|
||||
.and_then(|def_id| {
|
||||
if match_def_path(cx, def_id, &paths::CMP_MIN) {
|
||||
fetch_const(cx, args, MinMax::Min)
|
||||
} else if match_def_path(cx, def_id, &paths::CMP_MAX) {
|
||||
fetch_const(cx, args, MinMax::Max)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(ref path, _, ref args, _) => {
|
||||
if_chain! {
|
||||
if let [obj, _] = args;
|
||||
if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD);
|
||||
then {
|
||||
if path.ident.as_str() == sym!(max).as_str() {
|
||||
fetch_const(cx, args, MinMax::Max)
|
||||
} else if path.ident.as_str() == sym!(min).as_str() {
|
||||
fetch_const(cx, args, MinMax::Min)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -271,7 +271,7 @@ impl EarlyLintPass for MiscEarlyLints {
|
|||
cx,
|
||||
BUILTIN_TYPE_SHADOW,
|
||||
param.ident.span,
|
||||
&format!("This generic shadows the built-in type `{}`", name),
|
||||
&format!("this generic shadows the built-in type `{}`", name),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -298,9 +298,9 @@ impl EarlyLintPass for MiscEarlyLints {
|
|||
cx,
|
||||
UNNEEDED_FIELD_PATTERN,
|
||||
pat.span,
|
||||
"All the struct fields are matched to a wildcard pattern, consider using `..`.",
|
||||
"all the struct fields are matched to a wildcard pattern, consider using `..`",
|
||||
None,
|
||||
&format!("Try with `{} {{ .. }}` instead", type_name),
|
||||
&format!("try with `{} {{ .. }}` instead", type_name),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -313,7 +313,7 @@ impl EarlyLintPass for MiscEarlyLints {
|
|||
cx,
|
||||
UNNEEDED_FIELD_PATTERN,
|
||||
field.span,
|
||||
"You matched a field with a wildcard pattern. Consider using `..` instead",
|
||||
"you matched a field with a wildcard pattern, consider using `..` instead",
|
||||
);
|
||||
} else {
|
||||
let mut normal = vec![];
|
||||
|
@ -333,10 +333,10 @@ impl EarlyLintPass for MiscEarlyLints {
|
|||
cx,
|
||||
UNNEEDED_FIELD_PATTERN,
|
||||
field.span,
|
||||
"You matched a field with a wildcard pattern. Consider using `..` \
|
||||
"you matched a field with a wildcard pattern, consider using `..` \
|
||||
instead",
|
||||
None,
|
||||
&format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
|
||||
&format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
118
clippy_lints/src/needless_arbitrary_self_type.rs
Normal file
118
clippy_lints/src/needless_arbitrary_self_type.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
use crate::utils::span_lint_and_sugg;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** The lint checks for `self` in fn parameters that
|
||||
/// specify the `Self`-type explicitly
|
||||
/// **Why is this bad?** Increases the amount and decreases the readability of code
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// enum ValType {
|
||||
/// I32,
|
||||
/// I64,
|
||||
/// F32,
|
||||
/// F64,
|
||||
/// }
|
||||
///
|
||||
/// impl ValType {
|
||||
/// pub fn bytes(self: Self) -> usize {
|
||||
/// match self {
|
||||
/// Self::I32 | Self::F32 => 4,
|
||||
/// Self::I64 | Self::F64 => 8,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be rewritten as
|
||||
///
|
||||
/// ```rust
|
||||
/// enum ValType {
|
||||
/// I32,
|
||||
/// I64,
|
||||
/// F32,
|
||||
/// F64,
|
||||
/// }
|
||||
///
|
||||
/// impl ValType {
|
||||
/// pub fn bytes(self) -> usize {
|
||||
/// match self {
|
||||
/// Self::I32 | Self::F32 => 4,
|
||||
/// Self::I64 | Self::F64 => 8,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub NEEDLESS_ARBITRARY_SELF_TYPE,
|
||||
complexity,
|
||||
"type of `self` parameter is already by default `Self`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(NeedlessArbitrarySelfType => [NEEDLESS_ARBITRARY_SELF_TYPE]);
|
||||
|
||||
enum Mode {
|
||||
Ref(Option<Lifetime>),
|
||||
Value,
|
||||
}
|
||||
|
||||
fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) {
|
||||
if_chain! {
|
||||
if let [segment] = &path.segments[..];
|
||||
if segment.ident.name == kw::SelfUpper;
|
||||
then {
|
||||
let self_param = match (binding_mode, mutbl) {
|
||||
(Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
|
||||
(Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name),
|
||||
(Mode::Ref(None), Mutability::Not) => "&self".to_string(),
|
||||
(Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name),
|
||||
(Mode::Value, Mutability::Mut) => "mut self".to_string(),
|
||||
(Mode::Value, Mutability::Not) => "self".to_string(),
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_ARBITRARY_SELF_TYPE,
|
||||
span,
|
||||
"the type of the `self` parameter does not need to be arbitrary",
|
||||
"consider to change this parameter to",
|
||||
self_param,
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for NeedlessArbitrarySelfType {
|
||||
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
|
||||
if !p.is_self() {
|
||||
return;
|
||||
}
|
||||
|
||||
match &p.ty.kind {
|
||||
TyKind::Path(None, path) => {
|
||||
if let PatKind::Ident(BindingMode::ByValue(mutbl), _, _) = p.pat.kind {
|
||||
check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl)
|
||||
}
|
||||
},
|
||||
TyKind::Rptr(lifetime, mut_ty) => {
|
||||
if_chain! {
|
||||
if let TyKind::Path(None, path) = &mut_ty.ty.kind;
|
||||
if let PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, _) = p.pat.kind;
|
||||
then {
|
||||
check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl)
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -243,7 +243,7 @@ fn check_comparison<'a, 'tcx>(
|
|||
cx,
|
||||
BOOL_COMPARISON,
|
||||
e.span,
|
||||
"This comparison might be written more concisely",
|
||||
"this comparison might be written more concisely",
|
||||
"try simplifying it as shown",
|
||||
format!(
|
||||
"{} != {}",
|
||||
|
|
|
@ -40,9 +40,8 @@ declare_clippy_lint! {
|
|||
/// assert_eq!(v.len(), 42);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// should be
|
||||
/// ```rust
|
||||
/// // should be
|
||||
/// fn foo(v: &[i32]) {
|
||||
/// assert_eq!(v.len(), 42);
|
||||
/// }
|
||||
|
@ -159,26 +158,19 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// * Exclude a type that is specifically bounded by `Borrow`.
|
||||
// * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
|
||||
// `serde::Serialize`)
|
||||
let (implements_borrow_trait, all_borrowable_trait) = {
|
||||
let preds = preds
|
||||
.iter()
|
||||
.filter(|t| t.self_ty() == ty)
|
||||
.collect::<Vec<_>>();
|
||||
let preds = preds.iter().filter(|t| t.self_ty() == ty).collect::<Vec<_>>();
|
||||
|
||||
(
|
||||
preds.iter().any(|t| t.def_id() == borrow_trait),
|
||||
!preds.is_empty() && {
|
||||
let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty);
|
||||
preds.iter().all(|t| {
|
||||
let ty_params = t
|
||||
.trait_ref
|
||||
.substs
|
||||
.iter()
|
||||
.skip(1)
|
||||
.collect::<Vec<_>>();
|
||||
let ty_params = t.trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
|
||||
implements_trait(cx, ty_empty_region, t.def_id(), &ty_params)
|
||||
})
|
||||
},
|
||||
|
|
|
@ -79,10 +79,10 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd {
|
|||
cx,
|
||||
NEG_CMP_OP_ON_PARTIAL_ORD,
|
||||
expr.span,
|
||||
"The use of negated comparison operators on partially ordered \
|
||||
types produces code that is hard to read and refactor. Please \
|
||||
"the use of negated comparison operators on partially ordered \
|
||||
types produces code that is hard to read and refactor, please \
|
||||
consider using the `partial_cmp` method instead, to make it \
|
||||
clear that the two values could be incomparable."
|
||||
clear that the two values could be incomparable"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
|
|||
if let Constant::Int(1) = consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit));
|
||||
if cx.typeck_results().expr_ty(exp).is_integral();
|
||||
then {
|
||||
span_lint(cx, NEG_MULTIPLY, span, "Negation by multiplying with `-1`");
|
||||
span_lint(cx, NEG_MULTIPLY, span, "negation by multiplying with `-1`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,13 +42,13 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional {
|
|||
if let BinOpKind::Lt = op.node {
|
||||
if let BinOpKind::Add = op2.node {
|
||||
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
|
||||
"You are trying to use classic C overflow conditions that will fail in Rust.");
|
||||
"you are trying to use classic C overflow conditions that will fail in Rust");
|
||||
}
|
||||
}
|
||||
if let BinOpKind::Gt = op.node {
|
||||
if let BinOpKind::Sub = op2.node {
|
||||
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
|
||||
"You are trying to use classic C underflow conditions that will fail in Rust.");
|
||||
"you are trying to use classic C underflow conditions that will fail in Rust");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,13 +67,13 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional {
|
|||
if let BinOpKind::Gt = op.node {
|
||||
if let BinOpKind::Add = op2.node {
|
||||
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
|
||||
"You are trying to use classic C overflow conditions that will fail in Rust.");
|
||||
"you are trying to use classic C overflow conditions that will fail in Rust");
|
||||
}
|
||||
}
|
||||
if let BinOpKind::Lt = op.node {
|
||||
if let BinOpKind::Sub = op2.node {
|
||||
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
|
||||
"You are trying to use classic C underflow conditions that will fail in Rust.");
|
||||
"you are trying to use classic C underflow conditions that will fail in Rust");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite {
|
|||
cx,
|
||||
PATH_BUF_PUSH_OVERWRITE,
|
||||
lit.span,
|
||||
"Calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
|
||||
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
|
||||
"try",
|
||||
format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
|
||||
Applicability::MachineApplicable,
|
||||
|
|
|
@ -145,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
|
|||
cx,
|
||||
CMP_NULL,
|
||||
expr.span,
|
||||
"Comparing with null is better expressed by the `.is_null()` method",
|
||||
"comparing with null is better expressed by the `.is_null()` method",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
|
|||
span_lint(cx,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
expr.span,
|
||||
&format!("It is more idiomatic to use `{}.iter().enumerate()`",
|
||||
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
|
||||
snippet(cx, iter_args[0].span, "_")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,13 +86,13 @@ impl EarlyLintPass for RedundantStaticLifetimes {
|
|||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if !item.span.from_expansion() {
|
||||
if let ItemKind::Const(_, ref var_type, _) = item.kind {
|
||||
self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime");
|
||||
self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
|
||||
// Don't check associated consts because `'static` cannot be elided on those (issue
|
||||
// #2438)
|
||||
}
|
||||
|
||||
if let ItemKind::Static(ref var_type, _, _) = item.kind {
|
||||
self.visit_type(var_type, cx, "Statics have by default a `'static` lifetime");
|
||||
self.visit_type(var_type, cx, "statics have by default a `'static` lifetime");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ impl EarlyLintPass for RefInDeref {
|
|||
cx,
|
||||
REF_IN_DEREF,
|
||||
object.span,
|
||||
"Creating a reference that is immediately dereferenced.",
|
||||
"creating a reference that is immediately dereferenced",
|
||||
"try this",
|
||||
snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(),
|
||||
applicability,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use crate::reexport::Name;
|
||||
use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
|
@ -10,6 +9,7 @@ use rustc_middle::lint::in_external_macro;
|
|||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for bindings that shadow other bindings already in
|
||||
|
@ -123,7 +123,7 @@ fn check_fn<'tcx>(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Bo
|
|||
check_expr(cx, &body.value, &mut bindings);
|
||||
}
|
||||
|
||||
fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Name, Span)>) {
|
||||
fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Symbol, Span)>) {
|
||||
let len = bindings.len();
|
||||
for stmt in block.stmts {
|
||||
match stmt.kind {
|
||||
|
@ -138,7 +138,7 @@ fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &
|
|||
bindings.truncate(len);
|
||||
}
|
||||
|
||||
fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Name, Span)>) {
|
||||
fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Symbol, Span)>) {
|
||||
if in_external_macro(cx.sess(), local.span) {
|
||||
return;
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ fn check_pat<'tcx>(
|
|||
pat: &'tcx Pat<'_>,
|
||||
init: Option<&'tcx Expr<'_>>,
|
||||
span: Span,
|
||||
bindings: &mut Vec<(Name, Span)>,
|
||||
bindings: &mut Vec<(Symbol, Span)>,
|
||||
) {
|
||||
// TODO: match more stuff / destructuring
|
||||
match pat.kind {
|
||||
|
@ -254,7 +254,7 @@ fn check_pat<'tcx>(
|
|||
|
||||
fn lint_shadow<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
name: Name,
|
||||
name: Symbol,
|
||||
span: Span,
|
||||
pattern_span: Span,
|
||||
init: Option<&'tcx Expr<'_>>,
|
||||
|
@ -315,7 +315,7 @@ fn lint_shadow<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Name, Span)>) {
|
||||
fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Symbol, Span)>) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
@ -351,7 +351,7 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut
|
|||
}
|
||||
}
|
||||
|
||||
fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Name, Span)>) {
|
||||
fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) {
|
||||
match ty.kind {
|
||||
TyKind::Slice(ref sty) => check_ty(cx, sty, bindings),
|
||||
TyKind::Array(ref fty, ref anon_const) => {
|
||||
|
@ -371,7 +371,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(
|
|||
}
|
||||
}
|
||||
|
||||
fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool {
|
||||
fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Box(ref inner) | ExprKind::AddrOf(_, _, ref inner) => is_self_shadow(name, inner),
|
||||
ExprKind::Block(ref block, _) => {
|
||||
|
@ -383,6 +383,6 @@ fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn path_eq_name(name: Name, path: &Path<'_>) -> bool {
|
||||
fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool {
|
||||
!path.is_global() && path.segments.len() == 1 && path.segments[0].ident.as_str() == name.as_str()
|
||||
}
|
||||
|
|
130
clippy_lints/src/stable_sort_primitive.rs
Normal file
130
clippy_lints/src/stable_sort_primitive.rs
Normal file
|
@ -0,0 +1,130 @@
|
|||
use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg};
|
||||
|
||||
use if_chain::if_chain;
|
||||
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// When sorting primitive values (integers, bools, chars, as well
|
||||
/// as arrays, slices, and tuples of such items), it is better to
|
||||
/// use an unstable sort than a stable sort.
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// Using a stable sort consumes more memory and cpu cycles. Because
|
||||
/// values which compare equal are identical, preserving their
|
||||
/// relative order (the guarantee that a stable sort provides) means
|
||||
/// nothing, while the extra costs still apply.
|
||||
///
|
||||
/// **Known problems:**
|
||||
/// None
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut vec = vec![2, 1, 3];
|
||||
/// vec.sort();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let mut vec = vec![2, 1, 3];
|
||||
/// vec.sort_unstable();
|
||||
/// ```
|
||||
pub STABLE_SORT_PRIMITIVE,
|
||||
perf,
|
||||
"use of sort() when sort_unstable() is equivalent"
|
||||
}
|
||||
|
||||
declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]);
|
||||
|
||||
/// The three "kinds" of sorts
|
||||
enum SortingKind {
|
||||
Vanilla,
|
||||
/* The other kinds of lint are currently commented out because they
|
||||
* can map distinct values to equal ones. If the key function is
|
||||
* provably one-to-one, or if the Cmp function conserves equality,
|
||||
* then they could be linted on, but I don't know if we can check
|
||||
* for that. */
|
||||
|
||||
/* ByKey,
|
||||
* ByCmp, */
|
||||
}
|
||||
impl SortingKind {
|
||||
/// The name of the stable version of this kind of sort
|
||||
fn stable_name(&self) -> &str {
|
||||
match self {
|
||||
SortingKind::Vanilla => "sort",
|
||||
/* SortingKind::ByKey => "sort_by_key",
|
||||
* SortingKind::ByCmp => "sort_by", */
|
||||
}
|
||||
}
|
||||
/// The name of the unstable version of this kind of sort
|
||||
fn unstable_name(&self) -> &str {
|
||||
match self {
|
||||
SortingKind::Vanilla => "sort_unstable",
|
||||
/* SortingKind::ByKey => "sort_unstable_by_key",
|
||||
* SortingKind::ByCmp => "sort_unstable_by", */
|
||||
}
|
||||
}
|
||||
/// Takes the name of a function call and returns the kind of sort
|
||||
/// that corresponds to that function name (or None if it isn't)
|
||||
fn from_stable_name(name: &str) -> Option<SortingKind> {
|
||||
match name {
|
||||
"sort" => Some(SortingKind::Vanilla),
|
||||
// "sort_by" => Some(SortingKind::ByCmp),
|
||||
// "sort_by_key" => Some(SortingKind::ByKey),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A detected instance of this lint
|
||||
struct LintDetection {
|
||||
slice_name: String,
|
||||
method: SortingKind,
|
||||
method_args: String,
|
||||
}
|
||||
|
||||
fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind;
|
||||
if let Some(slice) = &args.get(0);
|
||||
if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str());
|
||||
if is_slice_of_primitives(cx, slice);
|
||||
then {
|
||||
let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
|
||||
Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for StableSortPrimitive {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let Some(detection) = detect_stable_sort_primitive(cx, expr) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
STABLE_SORT_PRIMITIVE,
|
||||
expr.span,
|
||||
format!(
|
||||
"Use {} instead of {}",
|
||||
detection.method.unstable_name(),
|
||||
detection.method.stable_name()
|
||||
)
|
||||
.as_str(),
|
||||
"try",
|
||||
format!(
|
||||
"{}.{}({})",
|
||||
detection.slice_name,
|
||||
detection.method.unstable_name(),
|
||||
detection.method_args
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -64,26 +64,22 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
|
|||
| hir::BinOpKind::Gt => return,
|
||||
_ => {},
|
||||
}
|
||||
// Check if the binary expression is part of another bi/unary expression
|
||||
// or operator assignment as a child node
|
||||
let mut parent_expr = cx.tcx.hir().get_parent_node(expr.hir_id);
|
||||
while parent_expr != hir::CRATE_HIR_ID {
|
||||
if let hir::Node::Expr(e) = cx.tcx.hir().get(parent_expr) {
|
||||
match e.kind {
|
||||
hir::ExprKind::Binary(..)
|
||||
| hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _)
|
||||
| hir::ExprKind::AssignOp(..) => return,
|
||||
_ => {},
|
||||
|
||||
// Check for more than one binary operation in the implemented function
|
||||
// Linting when multiple operations are involved can result in false positives
|
||||
if_chain! {
|
||||
let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id);
|
||||
if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn);
|
||||
if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind;
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let mut visitor = BinaryExprVisitor { nb_binops: 0 };
|
||||
|
||||
then {
|
||||
walk_expr(&mut visitor, &body.value);
|
||||
if visitor.nb_binops > 1 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
parent_expr = cx.tcx.hir().get_parent_node(parent_expr);
|
||||
}
|
||||
// as a parent node
|
||||
let mut visitor = BinaryExprVisitor { in_binary_expr: false };
|
||||
walk_expr(&mut visitor, expr);
|
||||
|
||||
if visitor.in_binary_expr {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(impl_trait) = check_binop(
|
||||
|
@ -102,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
|
|||
cx,
|
||||
SUSPICIOUS_ARITHMETIC_IMPL,
|
||||
binop.span,
|
||||
&format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait),
|
||||
&format!("suspicious use of binary operator in `{}` impl", impl_trait),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -139,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
|
|||
cx,
|
||||
SUSPICIOUS_OP_ASSIGN_IMPL,
|
||||
binop.span,
|
||||
&format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait),
|
||||
&format!("suspicious use of binary operator in `{}` impl", impl_trait),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +177,7 @@ fn check_binop(
|
|||
}
|
||||
|
||||
struct BinaryExprVisitor {
|
||||
in_binary_expr: bool,
|
||||
nb_binops: u32,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for BinaryExprVisitor {
|
||||
|
@ -191,12 +187,13 @@ impl<'tcx> Visitor<'tcx> for BinaryExprVisitor {
|
|||
match expr.kind {
|
||||
hir::ExprKind::Binary(..)
|
||||
| hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _)
|
||||
| hir::ExprKind::AssignOp(..) => self.in_binary_expr = true,
|
||||
| hir::ExprKind::AssignOp(..) => self.nb_binops += 1,
|
||||
_ => {},
|
||||
}
|
||||
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
|
|
|
@ -2,9 +2,10 @@ use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_
|
|||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{GenericBound, Generics, WherePredicate};
|
||||
use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
|
||||
|
@ -29,6 +30,35 @@ declare_clippy_lint! {
|
|||
"Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for cases where generics are being used and multiple
|
||||
/// syntax specifications for trait bounds are used simultaneously.
|
||||
///
|
||||
/// **Why is this bad?** Duplicate bounds makes the code
|
||||
/// less readable than specifing them only once.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
|
||||
/// ```
|
||||
///
|
||||
/// Could be written as:
|
||||
///
|
||||
/// ```rust
|
||||
/// fn func<T: Clone + Default>(arg: T) {}
|
||||
/// ```
|
||||
/// or
|
||||
///
|
||||
/// ```rust
|
||||
/// fn func<T>(arg: T) where T: Clone + Default {}
|
||||
/// ```
|
||||
pub TRAIT_DUPLICATION_IN_BOUNDS,
|
||||
pedantic,
|
||||
"Check if the same trait bounds are specified twice during a function declaration"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TraitBounds {
|
||||
max_trait_bounds: u64,
|
||||
|
@ -41,10 +71,25 @@ impl TraitBounds {
|
|||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);
|
||||
impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for TraitBounds {
|
||||
fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
|
||||
self.check_type_repetition(cx, gen);
|
||||
check_trait_bound_duplication(cx, gen);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> {
|
||||
if let GenericBound::Trait(t, _) = bound {
|
||||
Some((t.trait_ref.path.res, t.span))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl TraitBounds {
|
||||
fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
|
||||
if in_macro(gen.span) {
|
||||
return;
|
||||
}
|
||||
|
@ -101,3 +146,48 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
|
||||
if in_macro(gen.span) || gen.params.is_empty() || gen.where_clause.predicates.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut map = FxHashMap::default();
|
||||
for param in gen.params {
|
||||
if let ParamName::Plain(ref ident) = param.name {
|
||||
let res = param
|
||||
.bounds
|
||||
.iter()
|
||||
.filter_map(get_trait_res_span_from_bound)
|
||||
.collect::<Vec<_>>();
|
||||
map.insert(*ident, res);
|
||||
}
|
||||
}
|
||||
|
||||
for predicate in gen.where_clause.predicates {
|
||||
if_chain! {
|
||||
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
|
||||
if !in_macro(bound_predicate.span);
|
||||
if let TyKind::Path(ref path) = bound_predicate.bounded_ty.kind;
|
||||
if let QPath::Resolved(_, Path { ref segments, .. }) = path;
|
||||
if let Some(segment) = segments.first();
|
||||
if let Some(trait_resolutions_direct) = map.get(&segment.ident);
|
||||
then {
|
||||
for (res_where, _) in bound_predicate.bounds.iter().filter_map(get_trait_res_span_from_bound) {
|
||||
if let Some((_, span_direct)) = trait_resolutions_direct
|
||||
.iter()
|
||||
.find(|(res_direct, _)| *res_direct == res_where) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
TRAIT_DUPLICATION_IN_BOUNDS,
|
||||
*span_direct,
|
||||
"this trait bound is already specified in the where clause",
|
||||
None,
|
||||
"consider removing this trait bound",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,12 +61,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// core::intrinsics::transmute::<*const [i32], *const [u16]>(p)
|
||||
/// ```rust
|
||||
/// # let p: *const [i32] = &[];
|
||||
/// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// p as *const [u16]
|
||||
/// # let p: *const [i32] = &[];
|
||||
/// p as *const [u16];
|
||||
/// ```
|
||||
pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
||||
complexity,
|
||||
|
@ -704,14 +706,14 @@ fn can_be_expressed_as_pointer_cast<'tcx>(
|
|||
from_ty: Ty<'tcx>,
|
||||
to_ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
use CastKind::*;
|
||||
use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
|
||||
matches!(
|
||||
check_cast(cx, e, from_ty, to_ty),
|
||||
Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast)
|
||||
)
|
||||
}
|
||||
|
||||
/// If a cast from from_ty to to_ty is valid, returns an Ok containing the kind of
|
||||
/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of
|
||||
/// the cast. In certain cases, including some invalid casts from array references
|
||||
/// to pointers, this may cause additional errors to be emitted and/or ICE error
|
||||
/// messages. This function will panic if that occurs.
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use crate::utils::{match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg};
|
||||
use crate::utils::{
|
||||
is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite,
|
||||
span_lint_and_sugg,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -65,19 +68,39 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
|
|||
if let Some(ref err_arg) = err_args.get(0);
|
||||
if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
|
||||
if match_qpath(err_fun_path, &paths::RESULT_ERR);
|
||||
if let Some(return_type) = find_err_return_type(cx, &expr.kind);
|
||||
|
||||
if let Some(return_ty) = find_return_type(cx, &expr.kind);
|
||||
then {
|
||||
let err_type = cx.typeck_results().expr_ty(err_arg);
|
||||
let prefix;
|
||||
let suffix;
|
||||
let err_ty;
|
||||
|
||||
if let Some(ty) = result_error_type(cx, return_ty) {
|
||||
prefix = "Err(";
|
||||
suffix = ")";
|
||||
err_ty = ty;
|
||||
} else if let Some(ty) = poll_result_error_type(cx, return_ty) {
|
||||
prefix = "Poll::Ready(Err(";
|
||||
suffix = "))";
|
||||
err_ty = ty;
|
||||
} else if let Some(ty) = poll_option_result_error_type(cx, return_ty) {
|
||||
prefix = "Poll::Ready(Some(Err(";
|
||||
suffix = ")))";
|
||||
err_ty = ty;
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let expr_err_ty = cx.typeck_results().expr_ty(err_arg);
|
||||
|
||||
let origin_snippet = if err_arg.span.from_expansion() {
|
||||
snippet_with_macro_callsite(cx, err_arg.span, "_")
|
||||
} else {
|
||||
snippet(cx, err_arg.span, "_")
|
||||
};
|
||||
let suggestion = if err_type == return_type {
|
||||
format!("return Err({})", origin_snippet)
|
||||
let suggestion = if err_ty == expr_err_ty {
|
||||
format!("return {}{}{}", prefix, origin_snippet, suffix)
|
||||
} else {
|
||||
format!("return Err({}.into())", origin_snippet)
|
||||
format!("return {}{}.into(){}", prefix, origin_snippet, suffix)
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
|
@ -94,27 +117,68 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
|
|||
}
|
||||
}
|
||||
|
||||
// In order to determine whether to suggest `.into()` or not, we need to find the error type the
|
||||
// function returns. To do that, we look for the From::from call (see tree above), and capture
|
||||
// its output type.
|
||||
fn find_err_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
|
||||
/// Finds function return type by examining return expressions in match arms.
|
||||
fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
|
||||
if let ExprKind::Match(_, ref arms, MatchSource::TryDesugar) = expr {
|
||||
arms.iter().find_map(|ty| find_err_return_type_arm(cx, ty))
|
||||
} else {
|
||||
None
|
||||
for arm in arms.iter() {
|
||||
if let ExprKind::Ret(Some(ref ret)) = arm.body.kind {
|
||||
return Some(cx.typeck_results().expr_ty(ret));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// Check for From::from in one of the match arms.
|
||||
fn find_err_return_type_arm<'tcx>(cx: &LateContext<'tcx>, arm: &'tcx Arm<'_>) -> Option<Ty<'tcx>> {
|
||||
/// Extracts the error type from Result<T, E>.
|
||||
fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let ExprKind::Ret(Some(ref err_ret)) = arm.body.kind;
|
||||
if let ExprKind::Call(ref from_error_path, ref from_error_args) = err_ret.kind;
|
||||
if let ExprKind::Path(ref from_error_fn) = from_error_path.kind;
|
||||
if match_qpath(from_error_fn, &paths::TRY_FROM_ERROR);
|
||||
if let Some(from_error_arg) = from_error_args.get(0);
|
||||
if let ty::Adt(_, subst) = ty.kind;
|
||||
if is_type_diagnostic_item(cx, ty, sym!(result_type));
|
||||
let err_ty = subst.type_at(1);
|
||||
then {
|
||||
Some(cx.typeck_results().expr_ty(from_error_arg))
|
||||
Some(err_ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the error type from Poll<Result<T, E>>.
|
||||
fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let ty::Adt(def, subst) = ty.kind;
|
||||
if match_def_path(cx, def.did, &paths::POLL);
|
||||
let ready_ty = subst.type_at(0);
|
||||
|
||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind;
|
||||
if cx.tcx.is_diagnostic_item(sym!(result_type), ready_def.did);
|
||||
let err_ty = ready_subst.type_at(1);
|
||||
|
||||
then {
|
||||
Some(err_ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the error type from Poll<Option<Result<T, E>>>.
|
||||
fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let ty::Adt(def, subst) = ty.kind;
|
||||
if match_def_path(cx, def.did, &paths::POLL);
|
||||
let ready_ty = subst.type_at(0);
|
||||
|
||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind;
|
||||
if cx.tcx.is_diagnostic_item(sym!(option_type), ready_def.did);
|
||||
let some_ty = ready_subst.type_at(0);
|
||||
|
||||
if let ty::Adt(some_def, some_subst) = some_ty.kind;
|
||||
if cx.tcx.is_diagnostic_item(sym!(result_type), some_def.did);
|
||||
let err_ty = some_subst.type_at(1);
|
||||
|
||||
then {
|
||||
Some(err_ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -181,8 +181,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
|
|||
self.cx,
|
||||
UNNECESSARY_UNWRAP,
|
||||
expr.span,
|
||||
&format!("You checked before that `{}()` cannot fail. \
|
||||
Instead of checking and unwrapping, it's better to use `if let` or `match`.",
|
||||
&format!("you checked before that `{}()` cannot fail, \
|
||||
instead of checking and unwrapping, it's better to use `if let` or `match`",
|
||||
method_name.ident.name),
|
||||
|diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); },
|
||||
);
|
||||
|
@ -191,7 +191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
|
|||
self.cx,
|
||||
PANICKING_UNWRAP,
|
||||
expr.span,
|
||||
&format!("This call to `{}()` will always panic.",
|
||||
&format!("this call to `{}()` will always panic",
|
||||
method_name.ident.name),
|
||||
|diag| { diag.span_label(unwrappable.check.span, "because of this check"); },
|
||||
);
|
||||
|
|
0
clippy_lints/src/utils/ast_utils.rs
Executable file → Normal file
0
clippy_lints/src/utils/ast_utils.rs
Executable file → Normal file
|
@ -75,12 +75,12 @@ pub fn get_attr<'a>(
|
|||
})
|
||||
.map_or_else(
|
||||
|| {
|
||||
sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
|
||||
sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute");
|
||||
false
|
||||
},
|
||||
|deprecation_status| {
|
||||
let mut diag =
|
||||
sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute");
|
||||
sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute");
|
||||
match *deprecation_status {
|
||||
DeprecationStatus::Deprecated => {
|
||||
diag.emit();
|
||||
|
|
|
@ -43,6 +43,7 @@ use rustc_infer::infer::TyCtxtInferExt;
|
|||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_mir::const_eval;
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::original_sp;
|
||||
use rustc_span::symbol::{self, kw, Symbol};
|
||||
|
@ -52,7 +53,6 @@ use rustc_trait_selection::traits::query::normalize::AtExt;
|
|||
use smallvec::SmallVec;
|
||||
|
||||
use crate::consts::{constant, Constant};
|
||||
use crate::reexport::Name;
|
||||
|
||||
/// Returns `true` if the two spans come from differing expansions (i.e., one is
|
||||
/// from a macro and one isn't).
|
||||
|
@ -150,7 +150,7 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str])
|
|||
}
|
||||
|
||||
/// Checks if an expression references a variable of the given name.
|
||||
pub fn match_var(expr: &Expr<'_>, var: Name) -> bool {
|
||||
pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
|
||||
if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
|
||||
if let [p] = path.segments {
|
||||
return p.ident.name == var;
|
||||
|
@ -420,7 +420,7 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
|||
}
|
||||
|
||||
/// Gets the name of the item the expression is in, if available.
|
||||
pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Name> {
|
||||
pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
|
||||
let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
|
||||
match cx.tcx.hir().find(parent_id) {
|
||||
Some(
|
||||
|
@ -433,7 +433,7 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Name> {
|
|||
}
|
||||
|
||||
/// Gets the name of a `Pat`, if any.
|
||||
pub fn get_pat_name(pat: &Pat<'_>) -> Option<Name> {
|
||||
pub fn get_pat_name(pat: &Pat<'_>) -> Option<Symbol> {
|
||||
match pat.kind {
|
||||
PatKind::Binding(.., ref spname, _) => Some(spname.name),
|
||||
PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
|
||||
|
@ -443,14 +443,14 @@ pub fn get_pat_name(pat: &Pat<'_>) -> Option<Name> {
|
|||
}
|
||||
|
||||
struct ContainsName {
|
||||
name: Name,
|
||||
name: Symbol,
|
||||
result: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for ContainsName {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_name(&mut self, _: Span, name: Name) {
|
||||
fn visit_name(&mut self, _: Span, name: Symbol) {
|
||||
if self.name == name {
|
||||
self.result = true;
|
||||
}
|
||||
|
@ -461,7 +461,7 @@ impl<'tcx> Visitor<'tcx> for ContainsName {
|
|||
}
|
||||
|
||||
/// Checks if an `Expr` contains a certain name.
|
||||
pub fn contains_name(name: Name, expr: &Expr<'_>) -> bool {
|
||||
pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
|
||||
let mut cn = ContainsName { name, result: false };
|
||||
cn.visit_expr(expr);
|
||||
cn.result
|
||||
|
@ -869,11 +869,19 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
|||
|
||||
/// Checks if an expression is constructing a tuple-like enum variant or struct
|
||||
pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
||||
cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty()
|
||||
}
|
||||
|
||||
if let ExprKind::Call(ref fun, _) = expr.kind {
|
||||
if let ExprKind::Path(ref qp) = fun.kind {
|
||||
let res = cx.qpath_res(qp, fun.hir_id);
|
||||
return match res {
|
||||
def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
|
||||
// FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210
|
||||
def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => {
|
||||
const_eval::is_const_fn(cx.tcx, def_id)
|
||||
},
|
||||
def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
|
||||
_ => false,
|
||||
};
|
||||
|
@ -1027,7 +1035,7 @@ pub fn is_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool
|
|||
cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
|
||||
}
|
||||
|
||||
pub fn get_arg_name(pat: &Pat<'_>) -> Option<Name> {
|
||||
pub fn get_arg_name(pat: &Pat<'_>) -> Option<Symbol> {
|
||||
match pat.kind {
|
||||
PatKind::Binding(.., ident, None) => Some(ident.name),
|
||||
PatKind::Ref(ref subpat, _) => get_arg_name(subpat),
|
||||
|
@ -1378,6 +1386,36 @@ pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bo
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point
|
||||
/// number type, a str, or an array, slice, or tuple of those types).
|
||||
pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
|
||||
match ty.kind {
|
||||
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
|
||||
ty::Ref(_, inner, _) if inner.kind == ty::Str => true,
|
||||
ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
|
||||
ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true iff the given expression is a slice of primitives (as defined in the
|
||||
/// `is_recursively_primitive_type` function).
|
||||
pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
|
||||
match expr_type.kind {
|
||||
ty::Slice(ref element_type)
|
||||
| ty::Ref(
|
||||
_,
|
||||
ty::TyS {
|
||||
kind: ty::Slice(ref element_type),
|
||||
..
|
||||
},
|
||||
_,
|
||||
) => is_recursively_primitive_type(element_type),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unwrap_cargo_metadata {
|
||||
($cx: ident, $lint: ident, $deps: expr) => {{
|
||||
|
|
|
@ -80,6 +80,7 @@ pub const PATH: [&str; 3] = ["std", "path", "Path"];
|
|||
pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
|
||||
pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
|
||||
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
|
||||
pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
|
||||
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
|
||||
pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
|
||||
pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
|
||||
|
@ -129,7 +130,6 @@ pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
|
|||
pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
|
||||
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
|
||||
pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
|
||||
pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"];
|
||||
pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
|
||||
pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"];
|
||||
pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
|
||||
|
|
|
@ -27,10 +27,7 @@ because that's clearly a non-descriptive name.
|
|||
|
||||
## Setup
|
||||
|
||||
When working on Clippy, you will need the current git master version of rustc,
|
||||
which can change rapidly. Make sure you're working near rust-clippy's master,
|
||||
and use the `setup-toolchain.sh` script to configure the appropriate toolchain
|
||||
for the Clippy directory.
|
||||
See the [Basics](basics.md#get-the-code) documentation.
|
||||
|
||||
## Getting Started
|
||||
|
||||
|
@ -38,12 +35,14 @@ There is a bit of boilerplate code that needs to be set up when creating a new
|
|||
lint. Fortunately, you can use the clippy dev tools to handle this for you. We
|
||||
are naming our new lint `foo_functions` (lints are generally written in snake
|
||||
case), and we don't need type information so it will have an early pass type
|
||||
(more on this later on). To get started on this lint you can run
|
||||
`cargo dev new_lint --name=foo_functions --pass=early --category=pedantic`
|
||||
(category will default to nursery if not provided). This command will create
|
||||
two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`,
|
||||
as well as run `cargo dev update_lints` to register the new lint. For cargo lints,
|
||||
two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`.
|
||||
(more on this later on). If you're not sure if the name you chose fits the lint,
|
||||
take a look at our [lint naming guidelines][lint_naming]. To get started on this
|
||||
lint you can run `cargo dev new_lint --name=foo_functions --pass=early
|
||||
--category=pedantic` (category will default to nursery if not provided). This
|
||||
command will create two files: `tests/ui/foo_functions.rs` and
|
||||
`clippy_lints/src/foo_functions.rs`, as well as run `cargo dev update_lints` to
|
||||
register the new lint. For cargo lints, two project hierarchies (fail/pass) will
|
||||
be created by default under `tests/ui-cargo`.
|
||||
|
||||
Next, we'll open up these files and add our lint!
|
||||
|
||||
|
@ -113,7 +112,7 @@ For cargo lints, the process of testing differs in that we are interested in
|
|||
the `Cargo.toml` manifest file. We also need a minimal crate associated
|
||||
with that manifest.
|
||||
|
||||
If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint`
|
||||
If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint`
|
||||
we will find by default two new crates, each with its manifest file:
|
||||
|
||||
* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error.
|
||||
|
|
112
doc/basics.md
Normal file
112
doc/basics.md
Normal file
|
@ -0,0 +1,112 @@
|
|||
# Basics for hacking on Clippy
|
||||
|
||||
This document explains the basics for hacking on Clippy. Besides others, this
|
||||
includes how to set-up the development environment, how to build and how to test
|
||||
Clippy. For a more in depth description on the codebase take a look at [Adding
|
||||
Lints] or [Common Tools].
|
||||
|
||||
[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
|
||||
[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md
|
||||
|
||||
- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy)
|
||||
- [Get the code](#get-the-code)
|
||||
- [Setup](#setup)
|
||||
- [Building and Testing](#building-and-testing)
|
||||
- [`cargo dev`](#cargo-dev)
|
||||
|
||||
## Get the Code
|
||||
|
||||
First, make sure you have checked out the latest version of Clippy. If this is
|
||||
your first time working on Clippy, create a fork of the repository and clone it
|
||||
afterwards with the following command:
|
||||
|
||||
```bash
|
||||
git clone git@github.com:<your-username>/rust-clippy
|
||||
```
|
||||
|
||||
If you've already cloned Clippy in the past, update it to the latest version:
|
||||
|
||||
```bash
|
||||
# upstream has to be the remote of the rust-lang/rust-clippy repo
|
||||
git fetch upstream
|
||||
# make sure that you are on the master branch
|
||||
git checkout master
|
||||
# rebase your master branch on the upstream master
|
||||
git rebase upstream/master
|
||||
# push to the master branch of your fork
|
||||
git push
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
Next we need to setup the toolchain to compile Clippy. Since Clippy heavily
|
||||
relies on compiler internals it is build with the latest rustc master. To get
|
||||
this toolchain, you can just use the `setup-toolchain.sh` script or use
|
||||
`rustup-toolchain-install-master`:
|
||||
|
||||
```bash
|
||||
sh setup-toolchain.sh
|
||||
# OR
|
||||
cargo install rustup-toolchain-install-master
|
||||
# For better IDE integration also add `-c rustfmt -c rust-src` (optional)
|
||||
rustup-toolchain-install-master -f -n master -c rustc-dev -c llvm-tools
|
||||
rustup override set master
|
||||
```
|
||||
|
||||
_Note:_ Sometimes you may get compiler errors when building Clippy, even if you
|
||||
didn't change anything. Normally those will be fixed by a maintainer in a few hours.
|
||||
|
||||
## Building and Testing
|
||||
|
||||
Once the `master` toolchain is installed, you can build and test Clippy like
|
||||
every other Rust project:
|
||||
|
||||
```bash
|
||||
cargo build # builds Clippy
|
||||
cargo test # tests Clippy
|
||||
```
|
||||
|
||||
Since Clippy's test suite is pretty big, there are some commands that only run a
|
||||
subset of Clippy's tests:
|
||||
|
||||
```bash
|
||||
# only run UI tests
|
||||
cargo uitest
|
||||
# only run UI tests starting with `test_`
|
||||
TESTNAME="test_" cargo uitest
|
||||
# only run dogfood tests
|
||||
cargo test --test dogfood
|
||||
```
|
||||
|
||||
If the output of a [UI test] differs from the expected output, you can update the
|
||||
reference file with:
|
||||
|
||||
```bash
|
||||
sh tests/ui/update-all-references.sh
|
||||
```
|
||||
|
||||
For example, this is necessary, if you fix a typo in an error message of a lint
|
||||
or if you modify a test file to add a test case.
|
||||
|
||||
_Note:_ This command may update more files than you intended. In that case only
|
||||
commit the files you wanted to update.
|
||||
|
||||
[UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests
|
||||
|
||||
## `cargo dev`
|
||||
|
||||
Clippy has some dev tools to make working on Clippy more convenient. These tools
|
||||
can be accessed through the `cargo dev` command. Available tools are listed
|
||||
below. To get more information about these commands, just call them with
|
||||
`--help`.
|
||||
|
||||
```bash
|
||||
# formats the whole Clippy codebase and all tests
|
||||
cargo dev fmt
|
||||
# register or update lint names/groups/...
|
||||
cargo dev update_lints
|
||||
# create a new lint and register it
|
||||
cargo dev new_lint
|
||||
# (experimental) Setup Clippy to work with rust-analyzer
|
||||
cargo dev ra-setup
|
||||
```
|
|
@ -360,6 +360,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
|||
deprecation: None,
|
||||
module: "derive",
|
||||
},
|
||||
Lint {
|
||||
name: "derive_ord_xor_partial_ord",
|
||||
group: "correctness",
|
||||
desc: "deriving `Ord` but implementing `PartialOrd` explicitly",
|
||||
deprecation: None,
|
||||
module: "derive",
|
||||
},
|
||||
Lint {
|
||||
name: "diverging_sub_expression",
|
||||
group: "complexity",
|
||||
|
@ -405,7 +412,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
|||
Lint {
|
||||
name: "drop_bounds",
|
||||
group: "correctness",
|
||||
desc: "Bounds of the form `T: Drop` are useless",
|
||||
desc: "bounds of the form `T: Drop` are useless",
|
||||
deprecation: None,
|
||||
module: "drop_bounds",
|
||||
},
|
||||
|
@ -1452,6 +1459,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
|||
deprecation: None,
|
||||
module: "bytecount",
|
||||
},
|
||||
Lint {
|
||||
name: "needless_arbitrary_self_type",
|
||||
group: "complexity",
|
||||
desc: "type of `self` parameter is already by default `Self`",
|
||||
deprecation: None,
|
||||
module: "needless_arbitrary_self_type",
|
||||
},
|
||||
Lint {
|
||||
name: "needless_bool",
|
||||
group: "complexity",
|
||||
|
@ -1928,6 +1942,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
|||
deprecation: None,
|
||||
module: "copies",
|
||||
},
|
||||
Lint {
|
||||
name: "same_item_push",
|
||||
group: "style",
|
||||
desc: "the same item is pushed inside of a for loop",
|
||||
deprecation: None,
|
||||
module: "loops",
|
||||
},
|
||||
Lint {
|
||||
name: "search_is_some",
|
||||
group: "complexity",
|
||||
|
@ -2026,6 +2047,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
|||
deprecation: None,
|
||||
module: "slow_vector_initialization",
|
||||
},
|
||||
Lint {
|
||||
name: "stable_sort_primitive",
|
||||
group: "perf",
|
||||
desc: "use of sort() when sort_unstable() is equivalent",
|
||||
deprecation: None,
|
||||
module: "stable_sort_primitive",
|
||||
},
|
||||
Lint {
|
||||
name: "string_add",
|
||||
group: "restriction",
|
||||
|
@ -2166,6 +2194,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
|||
deprecation: None,
|
||||
module: "misc",
|
||||
},
|
||||
Lint {
|
||||
name: "trait_duplication_in_bounds",
|
||||
group: "pedantic",
|
||||
desc: "Check if the same trait bounds are specified twice during a function declaration",
|
||||
deprecation: None,
|
||||
module: "trait_bounds",
|
||||
},
|
||||
Lint {
|
||||
name: "transmute_bytes_to_str",
|
||||
group: "complexity",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use compiletest_rs as compiletest;
|
||||
use compiletest_rs::common::Mode as TestMode;
|
||||
|
||||
use std::env::{self, set_var};
|
||||
use std::env::{self, set_var, var};
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
|
@ -136,7 +136,9 @@ fn run_ui_toml(config: &mut compiletest::Config) {
|
|||
|
||||
let tests = compiletest::make_tests(&config);
|
||||
|
||||
let manifest_dir = var("CARGO_MANIFEST_DIR").unwrap_or_default();
|
||||
let res = run_tests(&config, tests);
|
||||
set_var("CARGO_MANIFEST_DIR", &manifest_dir);
|
||||
match res {
|
||||
Ok(true) => {},
|
||||
Ok(false) => panic!("Some tests failed"),
|
||||
|
@ -147,9 +149,6 @@ fn run_ui_toml(config: &mut compiletest::Config) {
|
|||
}
|
||||
|
||||
fn run_ui_cargo(config: &mut compiletest::Config) {
|
||||
if cargo::is_rustc_test_suite() {
|
||||
return;
|
||||
}
|
||||
fn run_tests(
|
||||
config: &compiletest::Config,
|
||||
filter: &Option<String>,
|
||||
|
@ -217,6 +216,10 @@ fn run_ui_cargo(config: &mut compiletest::Config) {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
if cargo::is_rustc_test_suite() {
|
||||
return;
|
||||
}
|
||||
|
||||
config.mode = TestMode::Ui;
|
||||
config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: This function has a large number of lines.
|
||||
error: this function has a large number of lines
|
||||
--> $DIR/test.rs:18:1
|
||||
|
|
||||
LL | / fn too_many_lines() {
|
||||
|
@ -9,7 +9,7 @@ LL | | }
|
|||
|
|
||||
= note: `-D clippy::too-many-lines` implied by `-D warnings`
|
||||
|
||||
error: This function has a large number of lines.
|
||||
error: this function has a large number of lines
|
||||
--> $DIR/test.rs:38:1
|
||||
|
|
||||
LL | / fn comment_before_code() {
|
||||
|
|
|
@ -47,6 +47,7 @@ async fn not_good(x: &Mutex<u32>) -> u32 {
|
|||
first + second + third
|
||||
}
|
||||
|
||||
#[allow(clippy::manual_async_fn)]
|
||||
fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
|
||||
async move {
|
||||
let guard = x.lock().unwrap();
|
||||
|
|
|
@ -46,13 +46,13 @@ LL | | };
|
|||
| |_____^
|
||||
|
||||
error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.
|
||||
--> $DIR/await_holding_lock.rs:52:13
|
||||
--> $DIR/await_holding_lock.rs:53:13
|
||||
|
|
||||
LL | let guard = x.lock().unwrap();
|
||||
| ^^^^^
|
||||
|
|
||||
note: these are all the await points this lock is held through
|
||||
--> $DIR/await_holding_lock.rs:52:9
|
||||
--> $DIR/await_holding_lock.rs:53:9
|
||||
|
|
||||
LL | / let guard = x.lock().unwrap();
|
||||
LL | | baz().await
|
||||
|
|
|
@ -84,25 +84,25 @@ error: order comparisons between booleans can be simplified
|
|||
LL | if x > y {
|
||||
| ^^^^^ help: try simplifying it as shown: `x & !y`
|
||||
|
||||
error: This comparison might be written more concisely
|
||||
error: this comparison might be written more concisely
|
||||
--> $DIR/bool_comparison.rs:120:8
|
||||
|
|
||||
LL | if a == !b {};
|
||||
| ^^^^^^^ help: try simplifying it as shown: `a != b`
|
||||
|
||||
error: This comparison might be written more concisely
|
||||
error: this comparison might be written more concisely
|
||||
--> $DIR/bool_comparison.rs:121:8
|
||||
|
|
||||
LL | if !a == b {};
|
||||
| ^^^^^^^ help: try simplifying it as shown: `a != b`
|
||||
|
||||
error: This comparison might be written more concisely
|
||||
error: this comparison might be written more concisely
|
||||
--> $DIR/bool_comparison.rs:125:8
|
||||
|
|
||||
LL | if b == !a {};
|
||||
| ^^^^^^^ help: try simplifying it as shown: `b != a`
|
||||
|
||||
error: This comparison might be written more concisely
|
||||
error: this comparison might be written more concisely
|
||||
--> $DIR/bool_comparison.rs:126:8
|
||||
|
|
||||
LL | if !b == a {};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: This generic shadows the built-in type `u32`
|
||||
error: this generic shadows the built-in type `u32`
|
||||
--> $DIR/builtin-type-shadow.rs:4:8
|
||||
|
|
||||
LL | fn foo<u32>(a: u32) -> u32 {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: You appear to be counting bytes the naive way
|
||||
error: you appear to be counting bytes the naive way
|
||||
--> $DIR/bytecount.rs:5:13
|
||||
|
|
||||
LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, 0)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, 0)`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/bytecount.rs:1:8
|
||||
|
@ -10,17 +10,17 @@ note: the lint level is defined here
|
|||
LL | #[deny(clippy::naive_bytecount)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: You appear to be counting bytes the naive way
|
||||
error: you appear to be counting bytes the naive way
|
||||
--> $DIR/bytecount.rs:7:13
|
||||
|
|
||||
LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count((&x[..]), 0)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count((&x[..]), 0)`
|
||||
|
||||
error: You appear to be counting bytes the naive way
|
||||
error: you appear to be counting bytes the naive way
|
||||
--> $DIR/bytecount.rs:19:13
|
||||
|
|
||||
LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, b + 1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, b + 1)`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:17:13
|
||||
|
|
||||
LL | let _ = value <= (u32::max_value() as i64) && value >= 0;
|
||||
|
@ -6,91 +6,91 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0;
|
|||
|
|
||||
= note: `-D clippy::checked-conversions` implied by `-D warnings`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:18:13
|
||||
|
|
||||
LL | let _ = value <= (u32::MAX as i64) && value >= 0;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:22:13
|
||||
|
|
||||
LL | let _ = value <= i64::from(u16::max_value()) && value >= 0;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:23:13
|
||||
|
|
||||
LL | let _ = value <= i64::from(u16::MAX) && value >= 0;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:27:13
|
||||
|
|
||||
LL | let _ = value <= (u8::max_value() as isize) && value >= 0;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:28:13
|
||||
|
|
||||
LL | let _ = value <= (u8::MAX as isize) && value >= 0;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:34:13
|
||||
|
|
||||
LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:35:13
|
||||
|
|
||||
LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:39:13
|
||||
|
|
||||
LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:40:13
|
||||
|
|
||||
LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:46:13
|
||||
|
|
||||
LL | let _ = value <= i32::max_value() as u32;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:47:13
|
||||
|
|
||||
LL | let _ = value <= i32::MAX as u32;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:51:13
|
||||
|
|
||||
LL | let _ = value <= isize::max_value() as usize && value as i32 == 5;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:52:13
|
||||
|
|
||||
LL | let _ = value <= isize::MAX as usize && value as i32 == 5;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:56:13
|
||||
|
|
||||
LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
|
||||
|
||||
error: Checked cast can be simplified.
|
||||
error: checked cast can be simplified
|
||||
--> $DIR/checked_conversions.rs:57:13
|
||||
|
|
||||
LL | let _ = value <= u16::MAX as u32 && value as i32 == 5;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/complex_conditionals.rs:8:9
|
||||
|
|
||||
LL | if x.is_ok() && y.is_err() {
|
||||
|
@ -12,7 +12,7 @@ note: the lint level is defined here
|
|||
LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap_err()` will always panic.
|
||||
error: this call to `unwrap_err()` will always panic
|
||||
--> $DIR/complex_conditionals.rs:9:9
|
||||
|
|
||||
LL | if x.is_ok() && y.is_err() {
|
||||
|
@ -27,7 +27,7 @@ note: the lint level is defined here
|
|||
LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
error: this call to `unwrap()` will always panic
|
||||
--> $DIR/complex_conditionals.rs:10:9
|
||||
|
|
||||
LL | if x.is_ok() && y.is_err() {
|
||||
|
@ -36,7 +36,7 @@ LL | if x.is_ok() && y.is_err() {
|
|||
LL | y.unwrap(); // will panic
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/complex_conditionals.rs:11:9
|
||||
|
|
||||
LL | if x.is_ok() && y.is_err() {
|
||||
|
@ -45,7 +45,7 @@ LL | if x.is_ok() && y.is_err() {
|
|||
LL | y.unwrap_err(); // unnecessary
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
error: this call to `unwrap()` will always panic
|
||||
--> $DIR/complex_conditionals.rs:25:9
|
||||
|
|
||||
LL | if x.is_ok() || y.is_ok() {
|
||||
|
@ -54,7 +54,7 @@ LL | if x.is_ok() || y.is_ok() {
|
|||
LL | x.unwrap(); // will panic
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/complex_conditionals.rs:26:9
|
||||
|
|
||||
LL | if x.is_ok() || y.is_ok() {
|
||||
|
@ -63,7 +63,7 @@ LL | if x.is_ok() || y.is_ok() {
|
|||
LL | x.unwrap_err(); // unnecessary
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
error: this call to `unwrap()` will always panic
|
||||
--> $DIR/complex_conditionals.rs:27:9
|
||||
|
|
||||
LL | if x.is_ok() || y.is_ok() {
|
||||
|
@ -72,7 +72,7 @@ LL | if x.is_ok() || y.is_ok() {
|
|||
LL | y.unwrap(); // will panic
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/complex_conditionals.rs:28:9
|
||||
|
|
||||
LL | if x.is_ok() || y.is_ok() {
|
||||
|
@ -81,7 +81,7 @@ LL | if x.is_ok() || y.is_ok() {
|
|||
LL | y.unwrap_err(); // unnecessary
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/complex_conditionals.rs:32:9
|
||||
|
|
||||
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
|
||||
|
@ -89,7 +89,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
|
|||
LL | x.unwrap(); // unnecessary
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap_err()` will always panic.
|
||||
error: this call to `unwrap_err()` will always panic
|
||||
--> $DIR/complex_conditionals.rs:33:9
|
||||
|
|
||||
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
|
||||
|
@ -98,7 +98,7 @@ LL | x.unwrap(); // unnecessary
|
|||
LL | x.unwrap_err(); // will panic
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
error: this call to `unwrap()` will always panic
|
||||
--> $DIR/complex_conditionals.rs:34:9
|
||||
|
|
||||
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
|
||||
|
@ -107,7 +107,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
|
|||
LL | y.unwrap(); // will panic
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/complex_conditionals.rs:35:9
|
||||
|
|
||||
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
|
||||
|
@ -116,7 +116,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
|
|||
LL | y.unwrap_err(); // unnecessary
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/complex_conditionals.rs:36:9
|
||||
|
|
||||
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
|
||||
|
@ -125,7 +125,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
|
|||
LL | z.unwrap(); // unnecessary
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap_err()` will always panic.
|
||||
error: this call to `unwrap_err()` will always panic
|
||||
--> $DIR/complex_conditionals.rs:37:9
|
||||
|
|
||||
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
|
||||
|
@ -134,7 +134,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
|
|||
LL | z.unwrap_err(); // will panic
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
error: this call to `unwrap()` will always panic
|
||||
--> $DIR/complex_conditionals.rs:45:9
|
||||
|
|
||||
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
|
||||
|
@ -143,7 +143,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
|
|||
LL | x.unwrap(); // will panic
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/complex_conditionals.rs:46:9
|
||||
|
|
||||
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
|
||||
|
@ -152,7 +152,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
|
|||
LL | x.unwrap_err(); // unnecessary
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/complex_conditionals.rs:47:9
|
||||
|
|
||||
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
|
||||
|
@ -161,7 +161,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
|
|||
LL | y.unwrap(); // unnecessary
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap_err()` will always panic.
|
||||
error: this call to `unwrap_err()` will always panic
|
||||
--> $DIR/complex_conditionals.rs:48:9
|
||||
|
|
||||
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
|
||||
|
@ -170,7 +170,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
|
|||
LL | y.unwrap_err(); // will panic
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
error: this call to `unwrap()` will always panic
|
||||
--> $DIR/complex_conditionals.rs:49:9
|
||||
|
|
||||
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
|
||||
|
@ -179,7 +179,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
|
|||
LL | z.unwrap(); // will panic
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/complex_conditionals.rs:50:9
|
||||
|
|
||||
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/complex_conditionals_nested.rs:8:13
|
||||
|
|
||||
LL | if x.is_some() {
|
||||
|
@ -12,7 +12,7 @@ note: the lint level is defined here
|
|||
LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
error: this call to `unwrap()` will always panic
|
||||
--> $DIR/complex_conditionals_nested.rs:10:13
|
||||
|
|
||||
LL | if x.is_some() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/simple_conditionals.rs:39:9
|
||||
|
|
||||
LL | if x.is_some() {
|
||||
|
@ -12,7 +12,7 @@ note: the lint level is defined here
|
|||
LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
error: this call to `unwrap()` will always panic
|
||||
--> $DIR/simple_conditionals.rs:41:9
|
||||
|
|
||||
LL | if x.is_some() {
|
||||
|
@ -27,7 +27,7 @@ note: the lint level is defined here
|
|||
LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
error: this call to `unwrap()` will always panic
|
||||
--> $DIR/simple_conditionals.rs:44:9
|
||||
|
|
||||
LL | if x.is_none() {
|
||||
|
@ -35,7 +35,7 @@ LL | if x.is_none() {
|
|||
LL | x.unwrap(); // will panic
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/simple_conditionals.rs:46:9
|
||||
|
|
||||
LL | if x.is_none() {
|
||||
|
@ -44,7 +44,7 @@ LL | if x.is_none() {
|
|||
LL | x.unwrap(); // unnecessary
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/simple_conditionals.rs:7:13
|
||||
|
|
||||
LL | if $a.is_some() {
|
||||
|
@ -57,7 +57,7 @@ LL | m!(x);
|
|||
|
|
||||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/simple_conditionals.rs:54:9
|
||||
|
|
||||
LL | if x.is_ok() {
|
||||
|
@ -65,7 +65,7 @@ LL | if x.is_ok() {
|
|||
LL | x.unwrap(); // unnecessary
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap_err()` will always panic.
|
||||
error: this call to `unwrap_err()` will always panic
|
||||
--> $DIR/simple_conditionals.rs:55:9
|
||||
|
|
||||
LL | if x.is_ok() {
|
||||
|
@ -74,7 +74,7 @@ LL | x.unwrap(); // unnecessary
|
|||
LL | x.unwrap_err(); // will panic
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
error: this call to `unwrap()` will always panic
|
||||
--> $DIR/simple_conditionals.rs:57:9
|
||||
|
|
||||
LL | if x.is_ok() {
|
||||
|
@ -83,7 +83,7 @@ LL | if x.is_ok() {
|
|||
LL | x.unwrap(); // will panic
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/simple_conditionals.rs:58:9
|
||||
|
|
||||
LL | if x.is_ok() {
|
||||
|
@ -92,7 +92,7 @@ LL | if x.is_ok() {
|
|||
LL | x.unwrap_err(); // unnecessary
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
error: this call to `unwrap()` will always panic
|
||||
--> $DIR/simple_conditionals.rs:61:9
|
||||
|
|
||||
LL | if x.is_err() {
|
||||
|
@ -100,7 +100,7 @@ LL | if x.is_err() {
|
|||
LL | x.unwrap(); // will panic
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/simple_conditionals.rs:62:9
|
||||
|
|
||||
LL | if x.is_err() {
|
||||
|
@ -109,7 +109,7 @@ LL | x.unwrap(); // will panic
|
|||
LL | x.unwrap_err(); // unnecessary
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
|
||||
--> $DIR/simple_conditionals.rs:64:9
|
||||
|
|
||||
LL | if x.is_err() {
|
||||
|
@ -118,7 +118,7 @@ LL | if x.is_err() {
|
|||
LL | x.unwrap(); // unnecessary
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap_err()` will always panic.
|
||||
error: this call to `unwrap_err()` will always panic
|
||||
--> $DIR/simple_conditionals.rs:65:9
|
||||
|
|
||||
LL | if x.is_err() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Comparing with null is better expressed by the `.is_null()` method
|
||||
error: comparing with null is better expressed by the `.is_null()` method
|
||||
--> $DIR/cmp_null.rs:9:8
|
||||
|
|
||||
LL | if p == ptr::null() {
|
||||
|
@ -6,7 +6,7 @@ LL | if p == ptr::null() {
|
|||
|
|
||||
= note: `-D clippy::cmp-null` implied by `-D warnings`
|
||||
|
||||
error: Comparing with null is better expressed by the `.is_null()` method
|
||||
error: comparing with null is better expressed by the `.is_null()` method
|
||||
--> $DIR/cmp_null.rs:14:8
|
||||
|
|
||||
LL | if m == ptr::null_mut() {
|
||||
|
|
5
tests/ui/crashes/ice-5872.rs
Normal file
5
tests/ui/crashes/ice-5872.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
#![warn(clippy::needless_collect)]
|
||||
|
||||
fn main() {
|
||||
let _ = vec![1, 2, 3].into_iter().collect::<Vec<_>>().is_empty();
|
||||
}
|
10
tests/ui/crashes/ice-5872.stderr
Normal file
10
tests/ui/crashes/ice-5872.stderr
Normal file
|
@ -0,0 +1,10 @@
|
|||
error: avoid using `collect()` when not needed
|
||||
--> $DIR/ice-5872.rs:4:39
|
||||
|
|
||||
LL | let _ = vec![1, 2, 3].into_iter().collect::<Vec<_>>().is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
|
||||
|
|
||||
= note: `-D clippy::needless-collect` implied by `-D warnings`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
error: Calling `std::string::String::default()` is more clear than this expression
|
||||
error: calling `std::string::String::default()` is more clear than this expression
|
||||
--> $DIR/default_trait_access.rs:8:22
|
||||
|
|
||||
LL | let s1: String = Default::default();
|
||||
|
@ -6,43 +6,43 @@ LL | let s1: String = Default::default();
|
|||
|
|
||||
= note: `-D clippy::default-trait-access` implied by `-D warnings`
|
||||
|
||||
error: Calling `std::string::String::default()` is more clear than this expression
|
||||
error: calling `std::string::String::default()` is more clear than this expression
|
||||
--> $DIR/default_trait_access.rs:12:22
|
||||
|
|
||||
LL | let s3: String = D2::default();
|
||||
| ^^^^^^^^^^^^^ help: try: `std::string::String::default()`
|
||||
|
||||
error: Calling `std::string::String::default()` is more clear than this expression
|
||||
error: calling `std::string::String::default()` is more clear than this expression
|
||||
--> $DIR/default_trait_access.rs:14:22
|
||||
|
|
||||
LL | let s4: String = std::default::Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
|
||||
|
||||
error: Calling `std::string::String::default()` is more clear than this expression
|
||||
error: calling `std::string::String::default()` is more clear than this expression
|
||||
--> $DIR/default_trait_access.rs:18:22
|
||||
|
|
||||
LL | let s6: String = default::Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
|
||||
|
||||
error: Calling `GenericDerivedDefault<std::string::String>::default()` is more clear than this expression
|
||||
error: calling `GenericDerivedDefault<std::string::String>::default()` is more clear than this expression
|
||||
--> $DIR/default_trait_access.rs:28:46
|
||||
|
|
||||
LL | let s11: GenericDerivedDefault<String> = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault<std::string::String>::default()`
|
||||
|
||||
error: Calling `TupleDerivedDefault::default()` is more clear than this expression
|
||||
error: calling `TupleDerivedDefault::default()` is more clear than this expression
|
||||
--> $DIR/default_trait_access.rs:34:36
|
||||
|
|
||||
LL | let s14: TupleDerivedDefault = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()`
|
||||
|
||||
error: Calling `ArrayDerivedDefault::default()` is more clear than this expression
|
||||
error: calling `ArrayDerivedDefault::default()` is more clear than this expression
|
||||
--> $DIR/default_trait_access.rs:36:36
|
||||
|
|
||||
LL | let s15: ArrayDerivedDefault = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()`
|
||||
|
||||
error: Calling `TupleStructDerivedDefault::default()` is more clear than this expression
|
||||
error: calling `TupleStructDerivedDefault::default()` is more clear than this expression
|
||||
--> $DIR/default_trait_access.rs:40:42
|
||||
|
|
||||
LL | let s17: TupleStructDerivedDefault = Default::default();
|
||||
|
|
68
tests/ui/derive_ord_xor_partial_ord.rs
Normal file
68
tests/ui/derive_ord_xor_partial_ord.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
#![warn(clippy::derive_ord_xor_partial_ord)]
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq)]
|
||||
struct DeriveBoth;
|
||||
|
||||
impl PartialEq<u64> for DeriveBoth {
|
||||
fn eq(&self, _: &u64) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<u64> for DeriveBoth {
|
||||
fn partial_cmp(&self, _: &u64) -> Option<Ordering> {
|
||||
Some(Ordering::Equal)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialEq, Eq)]
|
||||
struct DeriveOrd;
|
||||
|
||||
impl PartialOrd for DeriveOrd {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(other.cmp(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialEq, Eq)]
|
||||
struct DeriveOrdWithExplicitTypeVariable;
|
||||
|
||||
impl PartialOrd<DeriveOrdWithExplicitTypeVariable> for DeriveOrdWithExplicitTypeVariable {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(other.cmp(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialOrd, PartialEq, Eq)]
|
||||
struct DerivePartialOrd;
|
||||
|
||||
impl std::cmp::Ord for DerivePartialOrd {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
Ordering::Less
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialOrd, PartialEq, Eq)]
|
||||
struct ImplUserOrd;
|
||||
|
||||
trait Ord {}
|
||||
|
||||
// We don't want to lint on user-defined traits called `Ord`
|
||||
impl Ord for ImplUserOrd {}
|
||||
|
||||
mod use_ord {
|
||||
use std::cmp::{Ord, Ordering};
|
||||
|
||||
#[derive(PartialOrd, PartialEq, Eq)]
|
||||
struct DerivePartialOrdInUseOrd;
|
||||
|
||||
impl Ord for DerivePartialOrdInUseOrd {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
Ordering::Less
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
71
tests/ui/derive_ord_xor_partial_ord.stderr
Normal file
71
tests/ui/derive_ord_xor_partial_ord.stderr
Normal file
|
@ -0,0 +1,71 @@
|
|||
error: you are deriving `Ord` but have implemented `PartialOrd` explicitly
|
||||
--> $DIR/derive_ord_xor_partial_ord.rs:20:10
|
||||
|
|
||||
LL | #[derive(Ord, PartialEq, Eq)]
|
||||
| ^^^
|
||||
|
|
||||
= note: `-D clippy::derive-ord-xor-partial-ord` implied by `-D warnings`
|
||||
note: `PartialOrd` implemented here
|
||||
--> $DIR/derive_ord_xor_partial_ord.rs:23:1
|
||||
|
|
||||
LL | / impl PartialOrd for DeriveOrd {
|
||||
LL | | fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
LL | | Some(other.cmp(self))
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_^
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: you are deriving `Ord` but have implemented `PartialOrd` explicitly
|
||||
--> $DIR/derive_ord_xor_partial_ord.rs:29:10
|
||||
|
|
||||
LL | #[derive(Ord, PartialEq, Eq)]
|
||||
| ^^^
|
||||
|
|
||||
note: `PartialOrd` implemented here
|
||||
--> $DIR/derive_ord_xor_partial_ord.rs:32:1
|
||||
|
|
||||
LL | / impl PartialOrd<DeriveOrdWithExplicitTypeVariable> for DeriveOrdWithExplicitTypeVariable {
|
||||
LL | | fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
LL | | Some(other.cmp(self))
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_^
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: you are implementing `Ord` explicitly but have derived `PartialOrd`
|
||||
--> $DIR/derive_ord_xor_partial_ord.rs:41:1
|
||||
|
|
||||
LL | / impl std::cmp::Ord for DerivePartialOrd {
|
||||
LL | | fn cmp(&self, other: &Self) -> Ordering {
|
||||
LL | | Ordering::Less
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_^
|
||||
|
|
||||
note: `PartialOrd` implemented here
|
||||
--> $DIR/derive_ord_xor_partial_ord.rs:38:10
|
||||
|
|
||||
LL | #[derive(PartialOrd, PartialEq, Eq)]
|
||||
| ^^^^^^^^^^
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: you are implementing `Ord` explicitly but have derived `PartialOrd`
|
||||
--> $DIR/derive_ord_xor_partial_ord.rs:61:5
|
||||
|
|
||||
LL | / impl Ord for DerivePartialOrdInUseOrd {
|
||||
LL | | fn cmp(&self, other: &Self) -> Ordering {
|
||||
LL | | Ordering::Less
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
note: `PartialOrd` implemented here
|
||||
--> $DIR/derive_ord_xor_partial_ord.rs:58:14
|
||||
|
|
||||
LL | #[derive(PartialOrd, PartialEq, Eq)]
|
||||
| ^^^^^^^^^^
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
error: This binary expression can be simplified
|
||||
error: this binary expression can be simplified
|
||||
--> $DIR/double_comparison.rs:6:8
|
||||
|
|
||||
LL | if x == y || x < y {
|
||||
|
@ -6,43 +6,43 @@ LL | if x == y || x < y {
|
|||
|
|
||||
= note: `-D clippy::double-comparisons` implied by `-D warnings`
|
||||
|
||||
error: This binary expression can be simplified
|
||||
error: this binary expression can be simplified
|
||||
--> $DIR/double_comparison.rs:9:8
|
||||
|
|
||||
LL | if x < y || x == y {
|
||||
| ^^^^^^^^^^^^^^^ help: try: `x <= y`
|
||||
|
||||
error: This binary expression can be simplified
|
||||
error: this binary expression can be simplified
|
||||
--> $DIR/double_comparison.rs:12:8
|
||||
|
|
||||
LL | if x == y || x > y {
|
||||
| ^^^^^^^^^^^^^^^ help: try: `x >= y`
|
||||
|
||||
error: This binary expression can be simplified
|
||||
error: this binary expression can be simplified
|
||||
--> $DIR/double_comparison.rs:15:8
|
||||
|
|
||||
LL | if x > y || x == y {
|
||||
| ^^^^^^^^^^^^^^^ help: try: `x >= y`
|
||||
|
||||
error: This binary expression can be simplified
|
||||
error: this binary expression can be simplified
|
||||
--> $DIR/double_comparison.rs:18:8
|
||||
|
|
||||
LL | if x < y || x > y {
|
||||
| ^^^^^^^^^^^^^^ help: try: `x != y`
|
||||
|
||||
error: This binary expression can be simplified
|
||||
error: this binary expression can be simplified
|
||||
--> $DIR/double_comparison.rs:21:8
|
||||
|
|
||||
LL | if x > y || x < y {
|
||||
| ^^^^^^^^^^^^^^ help: try: `x != y`
|
||||
|
||||
error: This binary expression can be simplified
|
||||
error: this binary expression can be simplified
|
||||
--> $DIR/double_comparison.rs:24:8
|
||||
|
|
||||
LL | if x <= y && x >= y {
|
||||
| ^^^^^^^^^^^^^^^^ help: try: `x == y`
|
||||
|
||||
error: This binary expression can be simplified
|
||||
error: this binary expression can be simplified
|
||||
--> $DIR/double_comparison.rs:27:8
|
||||
|
|
||||
LL | if x >= y && x <= y {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Consider removing unnecessary double parentheses
|
||||
error: consider removing unnecessary double parentheses
|
||||
--> $DIR/double_parens.rs:15:5
|
||||
|
|
||||
LL | ((0))
|
||||
|
@ -6,31 +6,31 @@ LL | ((0))
|
|||
|
|
||||
= note: `-D clippy::double-parens` implied by `-D warnings`
|
||||
|
||||
error: Consider removing unnecessary double parentheses
|
||||
error: consider removing unnecessary double parentheses
|
||||
--> $DIR/double_parens.rs:19:14
|
||||
|
|
||||
LL | dummy_fn((0));
|
||||
| ^^^
|
||||
|
||||
error: Consider removing unnecessary double parentheses
|
||||
error: consider removing unnecessary double parentheses
|
||||
--> $DIR/double_parens.rs:23:20
|
||||
|
|
||||
LL | x.dummy_method((0));
|
||||
| ^^^
|
||||
|
||||
error: Consider removing unnecessary double parentheses
|
||||
error: consider removing unnecessary double parentheses
|
||||
--> $DIR/double_parens.rs:27:5
|
||||
|
|
||||
LL | ((1, 2))
|
||||
| ^^^^^^^^
|
||||
|
||||
error: Consider removing unnecessary double parentheses
|
||||
error: consider removing unnecessary double parentheses
|
||||
--> $DIR/double_parens.rs:31:5
|
||||
|
|
||||
LL | (())
|
||||
| ^^^^
|
||||
|
||||
error: Consider removing unnecessary double parentheses
|
||||
error: consider removing unnecessary double parentheses
|
||||
--> $DIR/double_parens.rs:53:16
|
||||
|
|
||||
LL | assert_eq!(((1, 2)), (1, 2), "Error");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue.
|
||||
error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue
|
||||
--> $DIR/drop_bounds.rs:2:11
|
||||
|
|
||||
LL | fn foo<T: Drop>() {}
|
||||
|
@ -6,7 +6,7 @@ LL | fn foo<T: Drop>() {}
|
|||
|
|
||||
= note: `#[deny(clippy::drop_bounds)]` on by default
|
||||
|
||||
error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue.
|
||||
error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue
|
||||
--> $DIR/drop_bounds.rs:5:8
|
||||
|
|
||||
LL | T: Drop,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
--> $DIR/empty_line_after_outer_attribute.rs:11:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
|
@ -9,7 +9,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) }
|
|||
|
|
||||
= note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
|
||||
|
||||
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
--> $DIR/empty_line_after_outer_attribute.rs:23:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
|
@ -17,7 +17,7 @@ LL | |
|
|||
LL | | fn with_one_newline() { assert!(true) }
|
||||
| |_
|
||||
|
||||
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
--> $DIR/empty_line_after_outer_attribute.rs:28:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
|
@ -26,7 +26,7 @@ LL | |
|
|||
LL | | fn with_two_newlines() { assert!(true) }
|
||||
| |_
|
||||
|
||||
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
--> $DIR/empty_line_after_outer_attribute.rs:35:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
|
@ -34,7 +34,7 @@ LL | |
|
|||
LL | | enum Baz {
|
||||
| |_
|
||||
|
||||
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
--> $DIR/empty_line_after_outer_attribute.rs:43:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
|
@ -42,7 +42,7 @@ LL | |
|
|||
LL | | struct Foo {
|
||||
| |_
|
||||
|
||||
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
||||
--> $DIR/empty_line_after_outer_attribute.rs:51:1
|
||||
|
|
||||
LL | / #[crate_type = "lib"]
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
#![allow(unused, dead_code, clippy::needless_lifetimes, clippy::needless_pass_by_value)]
|
||||
#![allow(
|
||||
unused,
|
||||
dead_code,
|
||||
clippy::needless_lifetimes,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::needless_arbitrary_self_type
|
||||
)]
|
||||
#![warn(clippy::extra_unused_lifetimes)]
|
||||
|
||||
fn empty() {}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: this lifetime isn't used in the function definition
|
||||
--> $DIR/extra_unused_lifetimes.rs:8:14
|
||||
--> $DIR/extra_unused_lifetimes.rs:14:14
|
||||
|
|
||||
LL | fn unused_lt<'a>(x: u8) {}
|
||||
| ^^
|
||||
|
@ -7,19 +7,19 @@ LL | fn unused_lt<'a>(x: u8) {}
|
|||
= note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings`
|
||||
|
||||
error: this lifetime isn't used in the function definition
|
||||
--> $DIR/extra_unused_lifetimes.rs:10:25
|
||||
--> $DIR/extra_unused_lifetimes.rs:16:25
|
||||
|
|
||||
LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) {
|
||||
| ^^
|
||||
|
||||
error: this lifetime isn't used in the function definition
|
||||
--> $DIR/extra_unused_lifetimes.rs:35:10
|
||||
--> $DIR/extra_unused_lifetimes.rs:41:10
|
||||
|
|
||||
LL | fn x<'a>(&self) {}
|
||||
| ^^
|
||||
|
||||
error: this lifetime isn't used in the function definition
|
||||
--> $DIR/extra_unused_lifetimes.rs:61:22
|
||||
--> $DIR/extra_unused_lifetimes.rs:67:22
|
||||
|
|
||||
LL | fn unused_lt<'a>(x: u8) {}
|
||||
| ^^
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: This function has a large number of lines.
|
||||
error: this function has a large number of lines
|
||||
--> $DIR/functions_maxlines.rs:58:1
|
||||
|
|
||||
LL | / fn bad_lines() {
|
||||
|
|
22
tests/ui/iter_skip_next.fixed
Normal file
22
tests/ui/iter_skip_next.fixed
Normal file
|
@ -0,0 +1,22 @@
|
|||
// run-rustfix
|
||||
// aux-build:option_helpers.rs
|
||||
|
||||
#![warn(clippy::iter_skip_next)]
|
||||
#![allow(clippy::blacklisted_name)]
|
||||
#![allow(clippy::iter_nth)]
|
||||
|
||||
extern crate option_helpers;
|
||||
|
||||
use option_helpers::IteratorFalsePositives;
|
||||
|
||||
/// Checks implementation of `ITER_SKIP_NEXT` lint
|
||||
fn main() {
|
||||
let some_vec = vec![0, 1, 2, 3];
|
||||
let _ = some_vec.iter().nth(42);
|
||||
let _ = some_vec.iter().cycle().nth(42);
|
||||
let _ = (1..10).nth(10);
|
||||
let _ = &some_vec[..].iter().nth(3);
|
||||
let foo = IteratorFalsePositives { foo: 0 };
|
||||
let _ = foo.skip(42).next();
|
||||
let _ = foo.filter().skip(42).next();
|
||||
}
|
|
@ -1,15 +1,17 @@
|
|||
// run-rustfix
|
||||
// aux-build:option_helpers.rs
|
||||
|
||||
#![warn(clippy::iter_skip_next)]
|
||||
#![allow(clippy::blacklisted_name)]
|
||||
#![allow(clippy::iter_nth)]
|
||||
|
||||
extern crate option_helpers;
|
||||
|
||||
use option_helpers::IteratorFalsePositives;
|
||||
|
||||
/// Checks implementation of `ITER_SKIP_NEXT` lint
|
||||
fn iter_skip_next() {
|
||||
let mut some_vec = vec![0, 1, 2, 3];
|
||||
fn main() {
|
||||
let some_vec = vec![0, 1, 2, 3];
|
||||
let _ = some_vec.iter().skip(42).next();
|
||||
let _ = some_vec.iter().cycle().skip(42).next();
|
||||
let _ = (1..10).skip(10).next();
|
||||
|
@ -18,5 +20,3 @@ fn iter_skip_next() {
|
|||
let _ = foo.skip(42).next();
|
||||
let _ = foo.filter().skip(42).next();
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,35 +1,28 @@
|
|||
error: called `skip(x).next()` on an iterator
|
||||
--> $DIR/iter_skip_next.rs:13:13
|
||||
--> $DIR/iter_skip_next.rs:15:28
|
||||
|
|
||||
LL | let _ = some_vec.iter().skip(42).next();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
|
||||
|
|
||||
= note: `-D clippy::iter-skip-next` implied by `-D warnings`
|
||||
= help: this is more succinctly expressed by calling `nth(x)`
|
||||
|
||||
error: called `skip(x).next()` on an iterator
|
||||
--> $DIR/iter_skip_next.rs:14:13
|
||||
--> $DIR/iter_skip_next.rs:16:36
|
||||
|
|
||||
LL | let _ = some_vec.iter().cycle().skip(42).next();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: this is more succinctly expressed by calling `nth(x)`
|
||||
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
|
||||
|
||||
error: called `skip(x).next()` on an iterator
|
||||
--> $DIR/iter_skip_next.rs:15:13
|
||||
--> $DIR/iter_skip_next.rs:17:20
|
||||
|
|
||||
LL | let _ = (1..10).skip(10).next();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: this is more succinctly expressed by calling `nth(x)`
|
||||
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)`
|
||||
|
||||
error: called `skip(x).next()` on an iterator
|
||||
--> $DIR/iter_skip_next.rs:16:14
|
||||
--> $DIR/iter_skip_next.rs:18:33
|
||||
|
|
||||
LL | let _ = &some_vec[..].iter().skip(3).next();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: this is more succinctly expressed by calling `nth(x)`
|
||||
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
pub struct PubOne;
|
||||
|
||||
impl PubOne {
|
||||
pub fn len(self: &Self) -> isize {
|
||||
pub fn len(&self) -> isize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl PubOne {
|
||||
// A second impl for this struct -- the error span shouldn't mention this.
|
||||
pub fn irrelevant(self: &Self) -> bool {
|
||||
pub fn irrelevant(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ pub struct PubAllowed;
|
|||
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
impl PubAllowed {
|
||||
pub fn len(self: &Self) -> isize {
|
||||
pub fn len(&self) -> isize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
@ -29,17 +29,17 @@ impl PubAllowed {
|
|||
// No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the
|
||||
// impl containing `len`.
|
||||
impl PubAllowed {
|
||||
pub fn irrelevant(self: &Self) -> bool {
|
||||
pub fn irrelevant(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PubTraitsToo {
|
||||
fn len(self: &Self) -> isize;
|
||||
fn len(&self) -> isize;
|
||||
}
|
||||
|
||||
impl PubTraitsToo for One {
|
||||
fn len(self: &Self) -> isize {
|
||||
fn len(&self) -> isize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
@ -47,11 +47,11 @@ impl PubTraitsToo for One {
|
|||
pub struct HasIsEmpty;
|
||||
|
||||
impl HasIsEmpty {
|
||||
pub fn len(self: &Self) -> isize {
|
||||
pub fn len(&self) -> isize {
|
||||
1
|
||||
}
|
||||
|
||||
fn is_empty(self: &Self) -> bool {
|
||||
fn is_empty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -59,11 +59,11 @@ impl HasIsEmpty {
|
|||
pub struct HasWrongIsEmpty;
|
||||
|
||||
impl HasWrongIsEmpty {
|
||||
pub fn len(self: &Self) -> isize {
|
||||
pub fn len(&self) -> isize {
|
||||
1
|
||||
}
|
||||
|
||||
pub fn is_empty(self: &Self, x: u32) -> bool {
|
||||
pub fn is_empty(&self, x: u32) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ impl HasWrongIsEmpty {
|
|||
struct NotPubOne;
|
||||
|
||||
impl NotPubOne {
|
||||
pub fn len(self: &Self) -> isize {
|
||||
pub fn len(&self) -> isize {
|
||||
// No error; `len` is pub but `NotPubOne` is not exported anyway.
|
||||
1
|
||||
}
|
||||
|
@ -80,19 +80,19 @@ impl NotPubOne {
|
|||
struct One;
|
||||
|
||||
impl One {
|
||||
fn len(self: &Self) -> isize {
|
||||
fn len(&self) -> isize {
|
||||
// No error; `len` is private; see issue #1085.
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
trait TraitsToo {
|
||||
fn len(self: &Self) -> isize;
|
||||
fn len(&self) -> isize;
|
||||
// No error; `len` is private; see issue #1085.
|
||||
}
|
||||
|
||||
impl TraitsToo for One {
|
||||
fn len(self: &Self) -> isize {
|
||||
fn len(&self) -> isize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
@ -100,11 +100,11 @@ impl TraitsToo for One {
|
|||
struct HasPrivateIsEmpty;
|
||||
|
||||
impl HasPrivateIsEmpty {
|
||||
pub fn len(self: &Self) -> isize {
|
||||
pub fn len(&self) -> isize {
|
||||
1
|
||||
}
|
||||
|
||||
fn is_empty(self: &Self) -> bool {
|
||||
fn is_empty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -112,16 +112,16 @@ impl HasPrivateIsEmpty {
|
|||
struct Wither;
|
||||
|
||||
pub trait WithIsEmpty {
|
||||
fn len(self: &Self) -> isize;
|
||||
fn is_empty(self: &Self) -> bool;
|
||||
fn len(&self) -> isize;
|
||||
fn is_empty(&self) -> bool;
|
||||
}
|
||||
|
||||
impl WithIsEmpty for Wither {
|
||||
fn len(self: &Self) -> isize {
|
||||
fn len(&self) -> isize {
|
||||
1
|
||||
}
|
||||
|
||||
fn is_empty(self: &Self) -> bool {
|
||||
fn is_empty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ error: item `PubOne` has a public `len` method but no corresponding `is_empty` m
|
|||
--> $DIR/len_without_is_empty.rs:6:1
|
||||
|
|
||||
LL | / impl PubOne {
|
||||
LL | | pub fn len(self: &Self) -> isize {
|
||||
LL | | pub fn len(&self) -> isize {
|
||||
LL | | 1
|
||||
LL | | }
|
||||
LL | | }
|
||||
|
@ -14,7 +14,7 @@ error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_e
|
|||
--> $DIR/len_without_is_empty.rs:37:1
|
||||
|
|
||||
LL | / pub trait PubTraitsToo {
|
||||
LL | | fn len(self: &Self) -> isize;
|
||||
LL | | fn len(&self) -> isize;
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
|
@ -22,7 +22,7 @@ error: item `HasIsEmpty` has a public `len` method but a private `is_empty` meth
|
|||
--> $DIR/len_without_is_empty.rs:49:1
|
||||
|
|
||||
LL | / impl HasIsEmpty {
|
||||
LL | | pub fn len(self: &Self) -> isize {
|
||||
LL | | pub fn len(&self) -> isize {
|
||||
LL | | 1
|
||||
LL | | }
|
||||
... |
|
||||
|
@ -34,7 +34,7 @@ error: item `HasWrongIsEmpty` has a public `len` method but no corresponding `is
|
|||
--> $DIR/len_without_is_empty.rs:61:1
|
||||
|
|
||||
LL | / impl HasWrongIsEmpty {
|
||||
LL | | pub fn len(self: &Self) -> isize {
|
||||
LL | | pub fn len(&self) -> isize {
|
||||
LL | | 1
|
||||
LL | | }
|
||||
... |
|
||||
|
|
|
@ -7,12 +7,12 @@ pub struct One;
|
|||
struct Wither;
|
||||
|
||||
trait TraitsToo {
|
||||
fn len(self: &Self) -> isize;
|
||||
fn len(&self) -> isize;
|
||||
// No error; `len` is private; see issue #1085.
|
||||
}
|
||||
|
||||
impl TraitsToo for One {
|
||||
fn len(self: &Self) -> isize {
|
||||
fn len(&self) -> isize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,11 @@ impl TraitsToo for One {
|
|||
pub struct HasIsEmpty;
|
||||
|
||||
impl HasIsEmpty {
|
||||
pub fn len(self: &Self) -> isize {
|
||||
pub fn len(&self) -> isize {
|
||||
1
|
||||
}
|
||||
|
||||
fn is_empty(self: &Self) -> bool {
|
||||
fn is_empty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -32,26 +32,26 @@ impl HasIsEmpty {
|
|||
pub struct HasWrongIsEmpty;
|
||||
|
||||
impl HasWrongIsEmpty {
|
||||
pub fn len(self: &Self) -> isize {
|
||||
pub fn len(&self) -> isize {
|
||||
1
|
||||
}
|
||||
|
||||
pub fn is_empty(self: &Self, x: u32) -> bool {
|
||||
pub fn is_empty(&self, x: u32) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WithIsEmpty {
|
||||
fn len(self: &Self) -> isize;
|
||||
fn is_empty(self: &Self) -> bool;
|
||||
fn len(&self) -> isize;
|
||||
fn is_empty(&self) -> bool;
|
||||
}
|
||||
|
||||
impl WithIsEmpty for Wither {
|
||||
fn len(self: &Self) -> isize {
|
||||
fn len(&self) -> isize {
|
||||
1
|
||||
}
|
||||
|
||||
fn is_empty(self: &Self) -> bool {
|
||||
fn is_empty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@ pub struct One;
|
|||
struct Wither;
|
||||
|
||||
trait TraitsToo {
|
||||
fn len(self: &Self) -> isize;
|
||||
fn len(&self) -> isize;
|
||||
// No error; `len` is private; see issue #1085.
|
||||
}
|
||||
|
||||
impl TraitsToo for One {
|
||||
fn len(self: &Self) -> isize {
|
||||
fn len(&self) -> isize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,11 @@ impl TraitsToo for One {
|
|||
pub struct HasIsEmpty;
|
||||
|
||||
impl HasIsEmpty {
|
||||
pub fn len(self: &Self) -> isize {
|
||||
pub fn len(&self) -> isize {
|
||||
1
|
||||
}
|
||||
|
||||
fn is_empty(self: &Self) -> bool {
|
||||
fn is_empty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -32,26 +32,26 @@ impl HasIsEmpty {
|
|||
pub struct HasWrongIsEmpty;
|
||||
|
||||
impl HasWrongIsEmpty {
|
||||
pub fn len(self: &Self) -> isize {
|
||||
pub fn len(&self) -> isize {
|
||||
1
|
||||
}
|
||||
|
||||
pub fn is_empty(self: &Self, x: u32) -> bool {
|
||||
pub fn is_empty(&self, x: u32) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WithIsEmpty {
|
||||
fn len(self: &Self) -> isize;
|
||||
fn is_empty(self: &Self) -> bool;
|
||||
fn len(&self) -> isize;
|
||||
fn is_empty(&self) -> bool;
|
||||
}
|
||||
|
||||
impl WithIsEmpty for Wither {
|
||||
fn len(self: &Self) -> isize {
|
||||
fn len(&self) -> isize {
|
||||
1
|
||||
}
|
||||
|
||||
fn is_empty(self: &Self) -> bool {
|
||||
fn is_empty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,10 +43,6 @@ impl S {
|
|||
42
|
||||
}
|
||||
|
||||
async fn meth_fut(&self) -> i32 { 42 }
|
||||
|
||||
async fn empty_fut(&self) {}
|
||||
|
||||
// should be ignored
|
||||
fn not_fut(&self) -> i32 {
|
||||
42
|
||||
|
@ -64,4 +60,40 @@ impl S {
|
|||
}
|
||||
}
|
||||
|
||||
// Tests related to lifetime capture
|
||||
|
||||
async fn elided(_: &i32) -> i32 { 42 }
|
||||
|
||||
// should be ignored
|
||||
fn elided_not_bound(_: &i32) -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
|
||||
async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 }
|
||||
|
||||
// should be ignored
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
mod issue_5765 {
|
||||
use std::future::Future;
|
||||
|
||||
struct A;
|
||||
impl A {
|
||||
fn f(&self) -> impl Future<Output = ()> {
|
||||
async {}
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let _future = {
|
||||
let a = A;
|
||||
a.f()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -51,14 +51,6 @@ impl S {
|
|||
}
|
||||
}
|
||||
|
||||
fn meth_fut(&self) -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
|
||||
fn empty_fut(&self) -> impl Future<Output = ()> {
|
||||
async {}
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
fn not_fut(&self) -> i32 {
|
||||
42
|
||||
|
@ -76,4 +68,44 @@ impl S {
|
|||
}
|
||||
}
|
||||
|
||||
// Tests related to lifetime capture
|
||||
|
||||
fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
|
||||
async { 42 }
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
fn elided_not_bound(_: &i32) -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
|
||||
fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b {
|
||||
async { 42 }
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
mod issue_5765 {
|
||||
use std::future::Future;
|
||||
|
||||
struct A;
|
||||
impl A {
|
||||
fn f(&self) -> impl Future<Output = ()> {
|
||||
async {}
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let _future = {
|
||||
let a = A;
|
||||
a.f()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -65,34 +65,34 @@ LL | let c = 21;
|
|||
...
|
||||
|
||||
error: this function can be simplified using the `async fn` syntax
|
||||
--> $DIR/manual_async_fn.rs:54:5
|
||||
--> $DIR/manual_async_fn.rs:73:1
|
||||
|
|
||||
LL | fn meth_fut(&self) -> impl Future<Output = i32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: make the function `async` and return the output of the future directly
|
||||
|
|
||||
LL | async fn meth_fut(&self) -> i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | async fn elided(_: &i32) -> i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: move the body of the async block to the enclosing function
|
||||
|
|
||||
LL | fn meth_fut(&self) -> impl Future<Output = i32> { 42 }
|
||||
| ^^^^^^
|
||||
LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ { 42 }
|
||||
| ^^^^^^
|
||||
|
||||
error: this function can be simplified using the `async fn` syntax
|
||||
--> $DIR/manual_async_fn.rs:58:5
|
||||
--> $DIR/manual_async_fn.rs:82:1
|
||||
|
|
||||
LL | fn empty_fut(&self) -> impl Future<Output = ()> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: make the function `async` and remove the return type
|
||||
help: make the function `async` and return the output of the future directly
|
||||
|
|
||||
LL | async fn empty_fut(&self) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: move the body of the async block to the enclosing function
|
||||
|
|
||||
LL | fn empty_fut(&self) -> impl Future<Output = ()> {}
|
||||
| ^^
|
||||
LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b { 42 }
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
|
|
|
@ -5,6 +5,20 @@
|
|||
#![allow(clippy::map_identity)]
|
||||
|
||||
fn main() {
|
||||
// mapping to Option on Iterator
|
||||
fn option_id(x: i8) -> Option<i8> {
|
||||
Some(x)
|
||||
}
|
||||
let option_id_ref: fn(i8) -> Option<i8> = option_id;
|
||||
let option_id_closure = |x| Some(x);
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect();
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect();
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect();
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect();
|
||||
|
||||
// mapping to Iterator on Iterator
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
|
||||
|
||||
// mapping to Option on Option
|
||||
let _: Option<_> = (Some(Some(1))).and_then(|x| x);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,20 @@
|
|||
#![allow(clippy::map_identity)]
|
||||
|
||||
fn main() {
|
||||
// mapping to Option on Iterator
|
||||
fn option_id(x: i8) -> Option<i8> {
|
||||
Some(x)
|
||||
}
|
||||
let option_id_ref: fn(i8) -> Option<i8> = option_id;
|
||||
let option_id_closure = |x| Some(x);
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
|
||||
|
||||
// mapping to Iterator on Iterator
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
|
||||
|
||||
// mapping to Option on Option
|
||||
let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
|
||||
}
|
||||
|
|
|
@ -1,16 +1,40 @@
|
|||
error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)`
|
||||
--> $DIR/map_flatten.rs:8:21
|
||||
error: called `map(..).flatten()` on an `Iterator`
|
||||
--> $DIR/map_flatten.rs:14:46
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)`
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)`
|
||||
|
|
||||
= note: `-D clippy::map-flatten` implied by `-D warnings`
|
||||
|
||||
error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)`
|
||||
--> $DIR/map_flatten.rs:9:24
|
||||
error: called `map(..).flatten()` on an `Iterator`
|
||||
--> $DIR/map_flatten.rs:15:46
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)`
|
||||
|
||||
error: called `map(..).flatten()` on an `Iterator`
|
||||
--> $DIR/map_flatten.rs:16:46
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)`
|
||||
|
||||
error: called `map(..).flatten()` on an `Iterator`
|
||||
--> $DIR/map_flatten.rs:17:46
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))`
|
||||
|
||||
error: called `map(..).flatten()` on an `Iterator`
|
||||
--> $DIR/map_flatten.rs:20:46
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)`
|
||||
|
||||
error: called `map(..).flatten()` on an `Option`
|
||||
--> $DIR/map_flatten.rs:23:39
|
||||
|
|
||||
LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
|
|
|
@ -6,6 +6,18 @@ use std::cmp::{max, min};
|
|||
|
||||
const LARGE: usize = 3;
|
||||
|
||||
struct NotOrd(u64);
|
||||
|
||||
impl NotOrd {
|
||||
fn min(self, x: u64) -> NotOrd {
|
||||
NotOrd(x)
|
||||
}
|
||||
|
||||
fn max(self, x: u64) -> NotOrd {
|
||||
NotOrd(x)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x;
|
||||
x = 2usize;
|
||||
|
@ -30,4 +42,24 @@ fn main() {
|
|||
max(min(s, "Apple"), "Zoo");
|
||||
|
||||
max("Apple", min(s, "Zoo")); // ok
|
||||
|
||||
let f = 3f32;
|
||||
x.min(1).max(3);
|
||||
x.max(3).min(1);
|
||||
f.max(3f32).min(1f32);
|
||||
|
||||
x.max(1).min(3); // ok
|
||||
x.min(3).max(1); // ok
|
||||
f.min(3f32).max(1f32); // ok
|
||||
|
||||
max(x.min(1), 3);
|
||||
min(x.max(1), 3); // ok
|
||||
|
||||
s.max("Zoo").min("Apple");
|
||||
s.min("Apple").max("Zoo");
|
||||
|
||||
s.min("Zoo").max("Apple"); // ok
|
||||
|
||||
let not_ord = NotOrd(1);
|
||||
not_ord.min(1).max(3); // ok
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:12:5
|
||||
--> $DIR/min_max.rs:24:5
|
||||
|
|
||||
LL | min(1, max(3, x));
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
@ -7,40 +7,76 @@ LL | min(1, max(3, x));
|
|||
= note: `-D clippy::min-max` implied by `-D warnings`
|
||||
|
||||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:13:5
|
||||
--> $DIR/min_max.rs:25:5
|
||||
|
|
||||
LL | min(max(3, x), 1);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:14:5
|
||||
--> $DIR/min_max.rs:26:5
|
||||
|
|
||||
LL | max(min(x, 1), 3);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:15:5
|
||||
--> $DIR/min_max.rs:27:5
|
||||
|
|
||||
LL | max(3, min(x, 1));
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:17:5
|
||||
--> $DIR/min_max.rs:29:5
|
||||
|
|
||||
LL | my_max(3, my_min(x, 1));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:29:5
|
||||
--> $DIR/min_max.rs:41:5
|
||||
|
|
||||
LL | min("Apple", max("Zoo", s));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:30:5
|
||||
--> $DIR/min_max.rs:42:5
|
||||
|
|
||||
LL | max(min(s, "Apple"), "Zoo");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:47:5
|
||||
|
|
||||
LL | x.min(1).max(3);
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:48:5
|
||||
|
|
||||
LL | x.max(3).min(1);
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:49:5
|
||||
|
|
||||
LL | f.max(3f32).min(1f32);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:55:5
|
||||
|
|
||||
LL | max(x.min(1), 3);
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:58:5
|
||||
|
|
||||
LL | s.max("Zoo").min("Apple");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `min`/`max` combination leads to constant result
|
||||
--> $DIR/min_max.rs:59:5
|
||||
|
|
||||
LL | s.min("Apple").max("Zoo");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
|
|
69
tests/ui/needless_arbitrary_self_type.fixed
Normal file
69
tests/ui/needless_arbitrary_self_type.fixed
Normal file
|
@ -0,0 +1,69 @@
|
|||
// run-rustfix
|
||||
|
||||
#![warn(clippy::needless_arbitrary_self_type)]
|
||||
#![allow(unused_mut, clippy::needless_lifetimes)]
|
||||
|
||||
pub enum ValType {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
impl ValType {
|
||||
pub fn bad(self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn good(self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_bad(mut self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_good(mut self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn ref_bad(&self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn ref_good(&self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn ref_bad_with_lifetime<'a>(&'a self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn ref_good_with_lifetime<'a>(&'a self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_ref_bad(&mut self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_ref_good(&mut self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_ref_bad_with_lifetime<'a>(&'a mut self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_ref_mut_good(mut self: &mut Self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
69
tests/ui/needless_arbitrary_self_type.rs
Normal file
69
tests/ui/needless_arbitrary_self_type.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
// run-rustfix
|
||||
|
||||
#![warn(clippy::needless_arbitrary_self_type)]
|
||||
#![allow(unused_mut, clippy::needless_lifetimes)]
|
||||
|
||||
pub enum ValType {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
impl ValType {
|
||||
pub fn bad(self: Self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn good(self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_bad(mut self: Self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_good(mut self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn ref_bad(self: &Self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn ref_good(&self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn ref_bad_with_lifetime<'a>(self: &'a Self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn ref_good_with_lifetime<'a>(&'a self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_ref_bad(self: &mut Self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_ref_good(&mut self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_ref_mut_good(mut self: &mut Self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
40
tests/ui/needless_arbitrary_self_type.stderr
Normal file
40
tests/ui/needless_arbitrary_self_type.stderr
Normal file
|
@ -0,0 +1,40 @@
|
|||
error: the type of the `self` parameter does not need to be arbitrary
|
||||
--> $DIR/needless_arbitrary_self_type.rs:12:16
|
||||
|
|
||||
LL | pub fn bad(self: Self) {
|
||||
| ^^^^^^^^^^ help: consider to change this parameter to: `self`
|
||||
|
|
||||
= note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings`
|
||||
|
||||
error: the type of the `self` parameter does not need to be arbitrary
|
||||
--> $DIR/needless_arbitrary_self_type.rs:20:20
|
||||
|
|
||||
LL | pub fn mut_bad(mut self: Self) {
|
||||
| ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self`
|
||||
|
||||
error: the type of the `self` parameter does not need to be arbitrary
|
||||
--> $DIR/needless_arbitrary_self_type.rs:28:20
|
||||
|
|
||||
LL | pub fn ref_bad(self: &Self) {
|
||||
| ^^^^^^^^^^^ help: consider to change this parameter to: `&self`
|
||||
|
||||
error: the type of the `self` parameter does not need to be arbitrary
|
||||
--> $DIR/needless_arbitrary_self_type.rs:36:38
|
||||
|
|
||||
LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) {
|
||||
| ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self`
|
||||
|
||||
error: the type of the `self` parameter does not need to be arbitrary
|
||||
--> $DIR/needless_arbitrary_self_type.rs:44:24
|
||||
|
|
||||
LL | pub fn mut_ref_bad(self: &mut Self) {
|
||||
| ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self`
|
||||
|
||||
error: the type of the `self` parameter does not need to be arbitrary
|
||||
--> $DIR/needless_arbitrary_self_type.rs:52:42
|
||||
|
|
||||
LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) {
|
||||
| ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
|
@ -5,11 +5,11 @@
|
|||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
|
||||
#[warn(clippy::needless_collect)]
|
||||
#[allow(unused_variables, clippy::iter_cloned_collect)]
|
||||
#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)]
|
||||
fn main() {
|
||||
let sample = [1; 5];
|
||||
let len = sample.iter().count();
|
||||
if sample.get(0).is_none() {
|
||||
if sample.iter().next().is_none() {
|
||||
// Empty
|
||||
}
|
||||
sample.iter().cloned().any(|x| x == 1);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
|
||||
#[warn(clippy::needless_collect)]
|
||||
#[allow(unused_variables, clippy::iter_cloned_collect)]
|
||||
#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)]
|
||||
fn main() {
|
||||
let sample = [1; 5];
|
||||
let len = sample.iter().collect::<Vec<_>>().len();
|
||||
|
|
|
@ -7,10 +7,10 @@ LL | let len = sample.iter().collect::<Vec<_>>().len();
|
|||
= note: `-D clippy::needless-collect` implied by `-D warnings`
|
||||
|
||||
error: avoid using `collect()` when not needed
|
||||
--> $DIR/needless_collect.rs:12:15
|
||||
--> $DIR/needless_collect.rs:12:22
|
||||
|
|
||||
LL | if sample.iter().collect::<Vec<_>>().is_empty() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get(0).is_none()`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
|
||||
|
||||
error: avoid using `collect()` when not needed
|
||||
--> $DIR/needless_collect.rs:15:28
|
||||
|
|
19
tests/ui/needless_collect_indirect.rs
Normal file
19
tests/ui/needless_collect_indirect.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
fn main() {
|
||||
let sample = [1; 5];
|
||||
let indirect_iter = sample.iter().collect::<Vec<_>>();
|
||||
indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
|
||||
let indirect_len = sample.iter().collect::<VecDeque<_>>();
|
||||
indirect_len.len();
|
||||
let indirect_empty = sample.iter().collect::<VecDeque<_>>();
|
||||
indirect_empty.is_empty();
|
||||
let indirect_contains = sample.iter().collect::<VecDeque<_>>();
|
||||
indirect_contains.contains(&&5);
|
||||
let indirect_negative = sample.iter().collect::<Vec<_>>();
|
||||
indirect_negative.len();
|
||||
indirect_negative
|
||||
.into_iter()
|
||||
.map(|x| (*x, *x + 1))
|
||||
.collect::<HashMap<_, _>>();
|
||||
}
|
55
tests/ui/needless_collect_indirect.stderr
Normal file
55
tests/ui/needless_collect_indirect.stderr
Normal file
|
@ -0,0 +1,55 @@
|
|||
error: avoid using `collect()` when not needed
|
||||
--> $DIR/needless_collect_indirect.rs:5:5
|
||||
|
|
||||
LL | / let indirect_iter = sample.iter().collect::<Vec<_>>();
|
||||
LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
|
||||
| |____^
|
||||
|
|
||||
= note: `-D clippy::needless-collect` implied by `-D warnings`
|
||||
help: Use the original Iterator instead of collecting it and then producing a new one
|
||||
|
|
||||
LL |
|
||||
LL | sample.iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
|
||||
|
|
||||
|
||||
error: avoid using `collect()` when not needed
|
||||
--> $DIR/needless_collect_indirect.rs:7:5
|
||||
|
|
||||
LL | / let indirect_len = sample.iter().collect::<VecDeque<_>>();
|
||||
LL | | indirect_len.len();
|
||||
| |____^
|
||||
|
|
||||
help: Take the original Iterator's count instead of collecting it and finding the length
|
||||
|
|
||||
LL |
|
||||
LL | sample.iter().count();
|
||||
|
|
||||
|
||||
error: avoid using `collect()` when not needed
|
||||
--> $DIR/needless_collect_indirect.rs:9:5
|
||||
|
|
||||
LL | / let indirect_empty = sample.iter().collect::<VecDeque<_>>();
|
||||
LL | | indirect_empty.is_empty();
|
||||
| |____^
|
||||
|
|
||||
help: Check if the original Iterator has anything instead of collecting it and seeing if it's empty
|
||||
|
|
||||
LL |
|
||||
LL | sample.iter().next().is_none();
|
||||
|
|
||||
|
||||
error: avoid using `collect()` when not needed
|
||||
--> $DIR/needless_collect_indirect.rs:11:5
|
||||
|
|
||||
LL | / let indirect_contains = sample.iter().collect::<VecDeque<_>>();
|
||||
LL | | indirect_contains.contains(&&5);
|
||||
| |____^
|
||||
|
|
||||
help: Check if the original Iterator contains an element instead of collecting then checking
|
||||
|
|
||||
LL |
|
||||
LL | sample.iter().any(|x| x == &&5);
|
||||
|
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
|
||||
error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
|
||||
--> $DIR/neg_cmp_op_on_partial_ord.rs:16:21
|
||||
|
|
||||
LL | let _not_less = !(a_value < another_value);
|
||||
|
@ -6,19 +6,19 @@ LL | let _not_less = !(a_value < another_value);
|
|||
|
|
||||
= note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings`
|
||||
|
||||
error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
|
||||
error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
|
||||
--> $DIR/neg_cmp_op_on_partial_ord.rs:19:30
|
||||
|
|
||||
LL | let _not_less_or_equal = !(a_value <= another_value);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
|
||||
error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
|
||||
--> $DIR/neg_cmp_op_on_partial_ord.rs:22:24
|
||||
|
|
||||
LL | let _not_greater = !(a_value > another_value);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
|
||||
error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
|
||||
--> $DIR/neg_cmp_op_on_partial_ord.rs:25:33
|
||||
|
|
||||
LL | let _not_greater_or_equal = !(a_value >= another_value);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Negation by multiplying with `-1`
|
||||
error: negation by multiplying with `-1`
|
||||
--> $DIR/neg_multiply.rs:27:5
|
||||
|
|
||||
LL | x * -1;
|
||||
|
@ -6,7 +6,7 @@ LL | x * -1;
|
|||
|
|
||||
= note: `-D clippy::neg-multiply` implied by `-D warnings`
|
||||
|
||||
error: Negation by multiplying with `-1`
|
||||
error: negation by multiplying with `-1`
|
||||
--> $DIR/neg_multiply.rs:29:5
|
||||
|
|
||||
LL | -1 * x;
|
||||
|
|
|
@ -22,9 +22,9 @@ struct HasOption {
|
|||
}
|
||||
|
||||
impl HasOption {
|
||||
fn do_option_nothing(self: &Self, value: usize) {}
|
||||
fn do_option_nothing(&self, value: usize) {}
|
||||
|
||||
fn do_option_plus_one(self: &Self, value: usize) -> usize {
|
||||
fn do_option_plus_one(&self, value: usize) -> usize {
|
||||
value + 1
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,9 @@ struct HasOption {
|
|||
}
|
||||
|
||||
impl HasOption {
|
||||
fn do_option_nothing(self: &Self, value: usize) {}
|
||||
fn do_option_nothing(&self, value: usize) {}
|
||||
|
||||
fn do_option_plus_one(self: &Self, value: usize) -> usize {
|
||||
fn do_option_plus_one(&self, value: usize) -> usize {
|
||||
value + 1
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,4 +116,12 @@ fn f() -> Option<()> {
|
|||
Some(())
|
||||
}
|
||||
|
||||
// Issue 5886 - const fn (with no arguments)
|
||||
pub fn skip_const_fn_with_no_args() {
|
||||
const fn foo() -> Option<i32> {
|
||||
Some(42)
|
||||
}
|
||||
let _ = None.or(foo());
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -116,4 +116,12 @@ fn f() -> Option<()> {
|
|||
Some(())
|
||||
}
|
||||
|
||||
// Issue 5886 - const fn (with no arguments)
|
||||
pub fn skip_const_fn_with_no_args() {
|
||||
const fn foo() -> Option<i32> {
|
||||
Some(42)
|
||||
}
|
||||
let _ = None.or(foo());
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: You are trying to use classic C overflow conditions that will fail in Rust.
|
||||
error: you are trying to use classic C overflow conditions that will fail in Rust
|
||||
--> $DIR/overflow_check_conditional.rs:8:8
|
||||
|
|
||||
LL | if a + b < a {}
|
||||
|
@ -6,43 +6,43 @@ LL | if a + b < a {}
|
|||
|
|
||||
= note: `-D clippy::overflow-check-conditional` implied by `-D warnings`
|
||||
|
||||
error: You are trying to use classic C overflow conditions that will fail in Rust.
|
||||
error: you are trying to use classic C overflow conditions that will fail in Rust
|
||||
--> $DIR/overflow_check_conditional.rs:9:8
|
||||
|
|
||||
LL | if a > a + b {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: You are trying to use classic C overflow conditions that will fail in Rust.
|
||||
error: you are trying to use classic C overflow conditions that will fail in Rust
|
||||
--> $DIR/overflow_check_conditional.rs:10:8
|
||||
|
|
||||
LL | if a + b < b {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: You are trying to use classic C overflow conditions that will fail in Rust.
|
||||
error: you are trying to use classic C overflow conditions that will fail in Rust
|
||||
--> $DIR/overflow_check_conditional.rs:11:8
|
||||
|
|
||||
LL | if b > a + b {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: You are trying to use classic C underflow conditions that will fail in Rust.
|
||||
error: you are trying to use classic C underflow conditions that will fail in Rust
|
||||
--> $DIR/overflow_check_conditional.rs:12:8
|
||||
|
|
||||
LL | if a - b > b {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: You are trying to use classic C underflow conditions that will fail in Rust.
|
||||
error: you are trying to use classic C underflow conditions that will fail in Rust
|
||||
--> $DIR/overflow_check_conditional.rs:13:8
|
||||
|
|
||||
LL | if b < a - b {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: You are trying to use classic C underflow conditions that will fail in Rust.
|
||||
error: you are trying to use classic C underflow conditions that will fail in Rust
|
||||
--> $DIR/overflow_check_conditional.rs:14:8
|
||||
|
|
||||
LL | if a - b > a {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: You are trying to use classic C underflow conditions that will fail in Rust.
|
||||
error: you are trying to use classic C underflow conditions that will fail in Rust
|
||||
--> $DIR/overflow_check_conditional.rs:15:8
|
||||
|
|
||||
LL | if a < a - b {}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: Calling `push` with '/' or '/' (file system root) will overwrite the previous path definition
|
||||
error: calling `push` with '/' or '/' (file system root) will overwrite the previous path definition
|
||||
--> $DIR/path_buf_push_overwrite.rs:7:12
|
||||
|
|
||||
LL | x.push("/bar");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: It is more idiomatic to use `v1.iter().enumerate()`
|
||||
error: it is more idiomatic to use `v1.iter().enumerate()`
|
||||
--> $DIR/range.rs:5:14
|
||||
|
|
||||
LL | let _x = v1.iter().zip(0..v1.len());
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue