## 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
[`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
[`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
[`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
[`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
[`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
## 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)
## 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!
});
// 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]
//! 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,
}
}
-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;
}
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?",
);
}
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)),
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,
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
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;
"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.
"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<'_>) {
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);
}
}
+/// 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) {
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))
cx,
DOUBLE_COMPARISONS,
span,
- "This binary expression can be simplified",
+ "this binary expression can be simplified",
"try",
sugg,
applicability,
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);
},
_ => {},
},
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);
}
}
},
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);
}
}
},
/// ```
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]);
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,
}
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")
}
}
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;
mod shadow;
mod single_component_path_imports;
mod slow_vector_initialization;
+mod stable_sort_primitive;
mod strings;
mod suspicious_trait_impl;
mod swap;
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.
&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,
&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,
&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,
&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,
&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,
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);
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![
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),
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),
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),
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),
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),
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),
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),
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),
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),
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! {
enum RefLt {
Unnamed,
Static,
- Named(Name),
+ Named(Symbol),
}
fn check_fn_inner<'tcx>(
}
struct LifetimeChecker {
- map: FxHashMap<Name, Span>,
+ map: FxHashMap<Symbol, Span>,
}
impl<'tcx> Visitor<'tcx> for LifetimeChecker {
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};
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};
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;
"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,
NEVER_LOOP,
MUT_RANGE_BOUND,
WHILE_IMMUTABLE_CONDITION,
+ SAME_ITEM_PUSH,
]);
impl<'tcx> LateLintPass<'tcx> for Loops {
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 {
}
}
+// 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)]
}
}
-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;
/// 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,
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,
}
struct LoopNestVisitor {
hir_id: HirId,
- iterator: Name,
+ iterator: Symbol,
nesting: Nesting,
}
}
}
-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 {
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;
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,
);
}
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,
}
}
-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!();
}
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};
/// ```
/// Use instead:
/// ```rust
- /// use std::future::Future;
- ///
/// async fn foo() -> i32 { 42 }
/// ```
pub MANUAL_ASYNC_FN,
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();
}
}
-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));
}
}
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;
///
/// **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
["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]),
);
}
-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,
+ );
+ }
}
}
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,
);
// 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,
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};
/// ```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,
}
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,
}
}
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),
);
}
}
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;
}
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![];
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(", ")),
);
}
}
--- /dev/null
+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)
+ }
+ }
+ },
+ _ => {},
+ }
+ }
+}
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!(
"{} != {}",
/// assert_eq!(v.len(), 42);
/// }
/// ```
- ///
+ /// should be
/// ```rust
- /// // should be
/// fn foo(v: &[i32]) {
/// assert_eq!(v.len(), 42);
/// }
}
}
+ //
// * 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)
})
},
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"
)
}
}
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`");
}
}
}
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");
}
}
}
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");
}
}
}
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,
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",
);
}
}
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, "_")));
}
}
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");
}
}
}
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,
-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::{
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
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 {
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;
}
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 {
fn lint_shadow<'tcx>(
cx: &LateContext<'tcx>,
- name: Name,
+ name: Symbol,
span: Span,
pattern_span: Span,
init: Option<&'tcx Expr<'_>>,
}
}
-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;
}
}
}
-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) => {
}
}
-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, _) => {
}
}
-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()
}
--- /dev/null
+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,
+ );
+ }
+ }
+}
| 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(
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),
);
}
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),
);
}
}
}
struct BinaryExprVisitor {
- in_binary_expr: bool,
+ nb_binops: u32,
}
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
}
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
"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,
}
}
-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;
}
}
}
}
+
+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",
+ );
+ }
+ }
+ }
+ }
+ }
+}
///
/// **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,
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.
-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! {
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(
}
}
-// 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(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(cx.typeck_results().expr_ty(from_error_arg))
+ Some(err_ty)
} else {
None
}
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"); },
);
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"); },
);
})
.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();
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};
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).
}
/// 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;
}
/// 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(
}
/// 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),
}
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;
}
}
/// 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
/// 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,
};
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),
})
}
+/// 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) => {{
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"];
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"];
## 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
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!
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.
--- /dev/null
+# 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
+```
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",
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",
},
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",
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",
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",
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",
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;
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"),
}
fn run_ui_cargo(config: &mut compiletest::Config) {
- if cargo::is_rustc_test_suite() {
- return;
- }
fn run_tests(
config: &compiletest::Config,
filter: &Option<String>,
Ok(result)
}
+ if cargo::is_rustc_test_suite() {
+ return;
+ }
+
config.mode = TestMode::Ui;
config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();
-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() {
|
= 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() {
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();
| |_____^
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
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 {};
-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 {
-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
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
-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;
|
= 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;
-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() {
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() {
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() {
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() {
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() {
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() {
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() {
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() {
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()) {
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()) {
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()) {
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()) {
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()) {
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()) {
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()) {
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()) {
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()) {
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()) {
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()) {
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()) {
-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() {
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() {
-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() {
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() {
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() {
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() {
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() {
|
= 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() {
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() {
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() {
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() {
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() {
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() {
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() {
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() {
-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() {
|
= 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() {
--- /dev/null
+#![warn(clippy::needless_collect)]
+
+fn main() {
+ let _ = vec![1, 2, 3].into_iter().collect::<Vec<_>>().is_empty();
+}
--- /dev/null
+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
+
-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();
|
= 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();
--- /dev/null
+#![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() {}
--- /dev/null
+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
+
-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 {
|
= 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 {
-error: Consider removing unnecessary double parentheses
+error: consider removing unnecessary double parentheses
--> $DIR/double_parens.rs:15:5
|
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");
-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>() {}
|
= 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,
-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"]
|
= 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"]
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"]
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"]
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"]
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"]
-#![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() {}
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) {}
| ^^
= 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) {}
| ^^
-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() {
--- /dev/null
+// 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();
+}
+// 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();
let _ = foo.skip(42).next();
let _ = foo.filter().skip(42).next();
}
-
-fn main() {}
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
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
}
}
#[allow(clippy::len_without_is_empty)]
impl PubAllowed {
- pub fn len(self: &Self) -> isize {
+ pub fn len(&self) -> isize {
1
}
}
// 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
}
}
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
}
}
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
}
}
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
}
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
}
}
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
}
}
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
}
}
--> $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 | | }
--> $DIR/len_without_is_empty.rs:37:1
|
LL | / pub trait PubTraitsToo {
-LL | | fn len(self: &Self) -> isize;
+LL | | fn len(&self) -> isize;
LL | | }
| |_^
--> $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 | | }
... |
--> $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 | | }
... |
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
}
}
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
}
}
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
}
}
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
}
}
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
}
}
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
}
}
42
}
- async fn meth_fut(&self) -> i32 { 42 }
-
- async fn empty_fut(&self) {}
-
// should be ignored
fn not_fut(&self) -> i32 {
42
}
}
+// 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() {}
}
}
- 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
}
}
+// 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() {}
...
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
#![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);
}
#![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();
}
-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
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;
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
}
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));
| ^^^^^^^^^^^^^^^^^
= 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
--- /dev/null
+// 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() {}
--- /dev/null
+// 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() {}
--- /dev/null
+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
+
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);
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();
= 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
--- /dev/null
+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<_, _>>();
+}
--- /dev/null
+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
+
-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);
|
= 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);
-error: Negation by multiplying with `-1`
+error: negation by multiplying with `-1`
--> $DIR/neg_multiply.rs:27:5
|
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;
}
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
}
}
}
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
}
}
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() {}
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() {}
-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 {}
|
= 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 {}
-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");
-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());
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:8:17
|
LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
|
= note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:12:21
|
LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:14:32
|
LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:14:47
|
LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:16:17
|
LL | const VAR_SIX: &'static u8 = &5;
| -^^^^^^^--- help: consider removing `'static`: `&u8`
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:18:20
|
LL | const VAR_HEIGHT: &'static Foo = &Foo {};
| -^^^^^^^---- help: consider removing `'static`: `&Foo`
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:20:19
|
LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
| -^^^^^^^----- help: consider removing `'static`: `&[u8]`
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:22:19
|
LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
| -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:24:19
|
LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
| -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:26:25
|
LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
| -^^^^^^^---- help: consider removing `'static`: `&str`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:30:29
|
LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:32:25
|
LL | static STATIC_VAR_SIX: &'static u8 = &5;
| -^^^^^^^--- help: consider removing `'static`: `&u8`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:34:28
|
LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
| -^^^^^^^---- help: consider removing `'static`: `&Foo`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:36:27
|
LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
| -^^^^^^^----- help: consider removing `'static`: `&[u8]`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:38:27
|
LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
| -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes.rs:40:27
|
LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:3:18
|
LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
|
= note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:3:30
|
LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:5:29
|
LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
| -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]`
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:5:39
|
LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
| -^^^^^^^---- help: consider removing `'static`: `&str`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:7:40
|
LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:7:55
|
LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:9:26
|
LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
| -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:9:38
|
LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:11:37
|
LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
| -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]`
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
--> $DIR/redundant_static_lifetimes_multiple.rs:11:47
|
LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
-error: Usage of deprecated attribute
+error: usage of deprecated attribute
--> $DIR/renamed_builtin_attr.rs:3:11
|
LL | #[clippy::cyclomatic_complexity = "1"]
}
impl HasResult {
- fn do_result_nothing(self: &Self, value: usize) {}
+ fn do_result_nothing(&self, value: usize) {}
- fn do_result_plus_one(self: &Self, value: usize) -> usize {
+ fn do_result_plus_one(&self, value: usize) -> usize {
value + 1
}
}
}
impl HasResult {
- fn do_result_nothing(self: &Self, value: usize) {}
+ fn do_result_nothing(&self, value: usize) {}
- fn do_result_plus_one(self: &Self, value: usize) -> usize {
+ fn do_result_plus_one(&self, value: usize) -> usize {
value + 1
}
}
--- /dev/null
+#![warn(clippy::same_item_push)]
+
+fn mutate_increment(x: &mut u8) -> u8 {
+ *x += 1;
+ *x
+}
+
+fn increment(x: u8) -> u8 {
+ x + 1
+}
+
+fn main() {
+ // Test for basic case
+ let mut spaces = Vec::with_capacity(10);
+ for _ in 0..10 {
+ spaces.push(vec![b' ']);
+ }
+
+ let mut vec2: Vec<u8> = Vec::new();
+ let item = 2;
+ for _ in 5..=20 {
+ vec2.push(item);
+ }
+
+ let mut vec3: Vec<u8> = Vec::new();
+ for _ in 0..15 {
+ let item = 2;
+ vec3.push(item);
+ }
+
+ let mut vec4: Vec<u8> = Vec::new();
+ for _ in 0..15 {
+ vec4.push(13);
+ }
+
+ // Suggestion should not be given as pushed variable can mutate
+ let mut vec5: Vec<u8> = Vec::new();
+ let mut item: u8 = 2;
+ for _ in 0..30 {
+ vec5.push(mutate_increment(&mut item));
+ }
+
+ let mut vec6: Vec<u8> = Vec::new();
+ let mut item: u8 = 2;
+ let mut item2 = &mut mutate_increment(&mut item);
+ for _ in 0..30 {
+ vec6.push(mutate_increment(item2));
+ }
+
+ let mut vec7: Vec<usize> = Vec::new();
+ for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() {
+ vec7.push(a);
+ }
+
+ let mut vec8: Vec<u8> = Vec::new();
+ for i in 0..30 {
+ vec8.push(increment(i));
+ }
+
+ let mut vec9: Vec<u8> = Vec::new();
+ for i in 0..30 {
+ vec9.push(i + i * i);
+ }
+
+ // Suggestion should not be given as there are multiple pushes that are not the same
+ let mut vec10: Vec<u8> = Vec::new();
+ let item: u8 = 2;
+ for _ in 0..30 {
+ vec10.push(item);
+ vec10.push(item * 2);
+ }
+
+ // Suggestion should not be given as Vec is not involved
+ for _ in 0..5 {
+ println!("Same Item Push");
+ }
+
+ struct A {
+ kind: u32,
+ }
+ let mut vec_a: Vec<A> = Vec::new();
+ for i in 0..30 {
+ vec_a.push(A { kind: i });
+ }
+ let mut vec12: Vec<u8> = Vec::new();
+ for a in vec_a {
+ vec12.push(2u8.pow(a.kind));
+ }
+}
--- /dev/null
+error: it looks like the same item is being pushed into this Vec
+ --> $DIR/same_item_push.rs:16:9
+ |
+LL | spaces.push(vec![b' ']);
+ | ^^^^^^
+ |
+ = note: `-D clippy::same-item-push` implied by `-D warnings`
+ = help: try using vec![vec![b' '];SIZE] or spaces.resize(NEW_SIZE, vec![b' '])
+
+error: it looks like the same item is being pushed into this Vec
+ --> $DIR/same_item_push.rs:22:9
+ |
+LL | vec2.push(item);
+ | ^^^^
+ |
+ = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item)
+
+error: it looks like the same item is being pushed into this Vec
+ --> $DIR/same_item_push.rs:28:9
+ |
+LL | vec3.push(item);
+ | ^^^^
+ |
+ = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item)
+
+error: it looks like the same item is being pushed into this Vec
+ --> $DIR/same_item_push.rs:33:9
+ |
+LL | vec4.push(13);
+ | ^^^^
+ |
+ = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13)
+
+error: aborting due to 4 previous errors
+
--- /dev/null
+// run-rustfix
+#![warn(clippy::stable_sort_primitive)]
+
+fn main() {
+ // positive examples
+ let mut vec = vec![1, 3, 2];
+ vec.sort_unstable();
+ let mut vec = vec![false, false, true];
+ vec.sort_unstable();
+ let mut vec = vec!['a', 'A', 'c'];
+ vec.sort_unstable();
+ let mut vec = vec!["ab", "cd", "ab", "bc"];
+ vec.sort_unstable();
+ let mut vec = vec![(2, 1), (1, 2), (2, 5)];
+ vec.sort_unstable();
+ let mut vec = vec![[2, 1], [1, 2], [2, 5]];
+ vec.sort_unstable();
+ let mut arr = [1, 3, 2];
+ arr.sort_unstable();
+ // Negative examples: behavior changes if made unstable
+ let mut vec = vec![1, 3, 2];
+ vec.sort_by_key(|i| i / 2);
+ vec.sort_by(|a, b| (a + b).cmp(&b));
+ // negative examples - Not of a primitive type
+ let mut vec_of_complex = vec![String::from("hello"), String::from("world!")];
+ vec_of_complex.sort();
+ vec_of_complex.sort_by_key(String::len);
+ let mut vec = vec![(String::from("hello"), String::from("world"))];
+ vec.sort();
+ let mut vec = vec![[String::from("hello"), String::from("world")]];
+ vec.sort();
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::stable_sort_primitive)]
+
+fn main() {
+ // positive examples
+ let mut vec = vec![1, 3, 2];
+ vec.sort();
+ let mut vec = vec![false, false, true];
+ vec.sort();
+ let mut vec = vec!['a', 'A', 'c'];
+ vec.sort();
+ let mut vec = vec!["ab", "cd", "ab", "bc"];
+ vec.sort();
+ let mut vec = vec![(2, 1), (1, 2), (2, 5)];
+ vec.sort();
+ let mut vec = vec![[2, 1], [1, 2], [2, 5]];
+ vec.sort();
+ let mut arr = [1, 3, 2];
+ arr.sort();
+ // Negative examples: behavior changes if made unstable
+ let mut vec = vec![1, 3, 2];
+ vec.sort_by_key(|i| i / 2);
+ vec.sort_by(|a, b| (a + b).cmp(&b));
+ // negative examples - Not of a primitive type
+ let mut vec_of_complex = vec![String::from("hello"), String::from("world!")];
+ vec_of_complex.sort();
+ vec_of_complex.sort_by_key(String::len);
+ let mut vec = vec![(String::from("hello"), String::from("world"))];
+ vec.sort();
+ let mut vec = vec![[String::from("hello"), String::from("world")]];
+ vec.sort();
+}
--- /dev/null
+error: Use sort_unstable instead of sort
+ --> $DIR/stable_sort_primitive.rs:7:5
+ |
+LL | vec.sort();
+ | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+ |
+ = note: `-D clippy::stable-sort-primitive` implied by `-D warnings`
+
+error: Use sort_unstable instead of sort
+ --> $DIR/stable_sort_primitive.rs:9:5
+ |
+LL | vec.sort();
+ | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+
+error: Use sort_unstable instead of sort
+ --> $DIR/stable_sort_primitive.rs:11:5
+ |
+LL | vec.sort();
+ | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+
+error: Use sort_unstable instead of sort
+ --> $DIR/stable_sort_primitive.rs:13:5
+ |
+LL | vec.sort();
+ | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+
+error: Use sort_unstable instead of sort
+ --> $DIR/stable_sort_primitive.rs:15:5
+ |
+LL | vec.sort();
+ | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+
+error: Use sort_unstable instead of sort
+ --> $DIR/stable_sort_primitive.rs:17:5
+ |
+LL | vec.sort();
+ | ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+
+error: Use sort_unstable instead of sort
+ --> $DIR/stable_sort_primitive.rs:19:5
+ |
+LL | arr.sort();
+ | ^^^^^^^^^^ help: try: `arr.sort_unstable()`
+
+error: aborting due to 7 previous errors
+
fn do_nothing(x: u32) -> u32 {
x
}
+
+struct MultipleBinops(u32);
+
+impl Add for MultipleBinops {
+ type Output = MultipleBinops;
+
+ // OK: multiple Binops in `add` impl
+ fn add(self, other: Self) -> Self::Output {
+ let mut result = self.0 + other.0;
+ if result >= u32::max_value() {
+ result -= u32::max_value();
+ }
+ MultipleBinops(result)
+ }
+}
+
+impl Mul for MultipleBinops {
+ type Output = MultipleBinops;
+
+ // OK: multiple Binops in `mul` impl
+ fn mul(self, other: Self) -> Self::Output {
+ let mut result: u32 = 0;
+ let size = std::cmp::max(self.0, other.0) as usize;
+ let mut v = vec![0; size + 1];
+ for i in 0..size + 1 {
+ result *= i as u32;
+ }
+ MultipleBinops(result)
+ }
+}
-error: Suspicious use of binary operator in `Add` impl
+error: suspicious use of binary operator in `Add` impl
--> $DIR/suspicious_arithmetic_impl.rs:11:20
|
LL | Foo(self.0 - other.0)
|
= note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings`
-error: Suspicious use of binary operator in `AddAssign` impl
+error: suspicious use of binary operator in `AddAssign` impl
--> $DIR/suspicious_arithmetic_impl.rs:17:23
|
LL | *self = *self - other;
|
= note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default
-error: Suspicious use of binary operator in `MulAssign` impl
+error: suspicious use of binary operator in `MulAssign` impl
--> $DIR/suspicious_arithmetic_impl.rs:30:16
|
LL | self.0 /= other.0;
--- /dev/null
+#![deny(clippy::trait_duplication_in_bounds)]
+
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
+
+fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+where
+ T: Clone,
+ T: Default,
+{
+ unimplemented!();
+}
+
+fn good_bar<T: Clone + Default>(arg: T) {
+ unimplemented!();
+}
+
+fn good_foo<T>(arg: T)
+where
+ T: Clone + Default,
+{
+ unimplemented!();
+}
+
+fn good_foobar<T: Default>(arg: T)
+where
+ T: Clone,
+{
+ unimplemented!();
+}
+
+fn main() {}
--- /dev/null
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:5:15
+ |
+LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+ | ^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/trait_duplication_in_bounds.rs:1:9
+ |
+LL | #![deny(clippy::trait_duplication_in_bounds)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = help: consider removing this trait bound
+
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:5:23
+ |
+LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+ | ^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: aborting due to 2 previous errors
+
use std::mem::{size_of, transmute};
-// rustc_typeck::check::cast contains documentation about when a cast `e as U` is
+// rustc_typeck::check::cast contains documentation about when a cast `e as U` is
// valid, which we quote from below.
fn main() {
// We should see an error message for each transmute, and no error messages for
// the casts, since the casts are the recommended fixes.
// e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
- let _ptr_i32_transmute = unsafe {
- usize::MAX as *const i32
- };
+ let _ptr_i32_transmute = unsafe { usize::MAX as *const i32 };
let ptr_i32 = usize::MAX as *const i32;
// e has type *T, U is *U_0, and either U_0: Sized ...
- let _ptr_i8_transmute = unsafe {
- ptr_i32 as *const i8
- };
+ let _ptr_i8_transmute = unsafe { ptr_i32 as *const i8 };
let _ptr_i8 = ptr_i32 as *const i8;
- let slice_ptr = &[0,1,2,3] as *const [i32];
+ let slice_ptr = &[0, 1, 2, 3] as *const [i32];
// ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
- let _ptr_to_unsized_transmute = unsafe {
- slice_ptr as *const [u16]
- };
+ let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u16] };
let _ptr_to_unsized = slice_ptr as *const [u16];
// TODO: We could try testing vtable casts here too, but maybe
// we should wait until std::raw::TraitObject is stabilized?
// e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
- let _usize_from_int_ptr_transmute = unsafe {
- ptr_i32 as usize
- };
+ let _usize_from_int_ptr_transmute = unsafe { ptr_i32 as usize };
let _usize_from_int_ptr = ptr_i32 as usize;
- let array_ref: &[i32; 4] = &[1,2,3,4];
+ let array_ref: &[i32; 4] = &[1, 2, 3, 4];
// e has type &[T; n] and U is *const T; array-ptr-cast
- let _array_ptr_transmute = unsafe {
- array_ref as *const [i32; 4]
- };
+ let _array_ptr_transmute = unsafe { array_ref as *const [i32; 4] };
let _array_ptr = array_ref as *const [i32; 4];
- fn foo(_: usize) -> u8 { 42 }
+ fn foo(_: usize) -> u8 {
+ 42
+ }
// e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast
- let _usize_ptr_transmute = unsafe {
- foo as *const usize
- };
+ let _usize_ptr_transmute = unsafe { foo as *const usize };
let _usize_ptr_transmute = foo as *const usize;
// e is a function pointer type and U is an integer; fptr-addr-cast
- let _usize_from_fn_ptr_transmute = unsafe {
- foo as usize
- };
+ let _usize_from_fn_ptr_transmute = unsafe { foo as usize };
let _usize_from_fn_ptr = foo as *const usize;
}
use std::mem::{size_of, transmute};
-// rustc_typeck::check::cast contains documentation about when a cast `e as U` is
+// rustc_typeck::check::cast contains documentation about when a cast `e as U` is
// valid, which we quote from below.
fn main() {
// We should see an error message for each transmute, and no error messages for
// the casts, since the casts are the recommended fixes.
// e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
- let _ptr_i32_transmute = unsafe {
- transmute::<usize, *const i32>(usize::MAX)
- };
+ let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) };
let ptr_i32 = usize::MAX as *const i32;
// e has type *T, U is *U_0, and either U_0: Sized ...
- let _ptr_i8_transmute = unsafe {
- transmute::<*const i32, *const i8>(ptr_i32)
- };
+ let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
let _ptr_i8 = ptr_i32 as *const i8;
- let slice_ptr = &[0,1,2,3] as *const [i32];
+ let slice_ptr = &[0, 1, 2, 3] as *const [i32];
// ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
- let _ptr_to_unsized_transmute = unsafe {
- transmute::<*const [i32], *const [u16]>(slice_ptr)
- };
+ let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) };
let _ptr_to_unsized = slice_ptr as *const [u16];
// TODO: We could try testing vtable casts here too, but maybe
// we should wait until std::raw::TraitObject is stabilized?
// e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
- let _usize_from_int_ptr_transmute = unsafe {
- transmute::<*const i32, usize>(ptr_i32)
- };
+ let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) };
let _usize_from_int_ptr = ptr_i32 as usize;
- let array_ref: &[i32; 4] = &[1,2,3,4];
+ let array_ref: &[i32; 4] = &[1, 2, 3, 4];
// e has type &[T; n] and U is *const T; array-ptr-cast
- let _array_ptr_transmute = unsafe {
- transmute::<&[i32; 4], *const [i32; 4]>(array_ref)
- };
+ let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
let _array_ptr = array_ref as *const [i32; 4];
- fn foo(_: usize) -> u8 { 42 }
+ fn foo(_: usize) -> u8 {
+ 42
+ }
// e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast
- let _usize_ptr_transmute = unsafe {
- transmute::<fn(usize) -> u8, *const usize>(foo)
- };
+ let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
let _usize_ptr_transmute = foo as *const usize;
// e is a function pointer type and U is an integer; fptr-addr-cast
- let _usize_from_fn_ptr_transmute = unsafe {
- transmute::<fn(usize) -> u8, usize>(foo)
- };
+ let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
let _usize_from_fn_ptr = foo as *const usize;
}
error: transmute from an integer to a pointer
- --> $DIR/transmutes_expressible_as_ptr_casts.rs:20:9
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:19:39
|
-LL | transmute::<usize, *const i32>(usize::MAX)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32`
+LL | let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32`
|
= note: `-D clippy::useless-transmute` implied by `-D warnings`
error: transmute from a pointer to a pointer
- --> $DIR/transmutes_expressible_as_ptr_casts.rs:26:9
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:23:38
|
-LL | transmute::<*const i32, *const i8>(ptr_i32)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8`
+LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8`
|
= note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
error: transmute from a pointer to a pointer
- --> $DIR/transmutes_expressible_as_ptr_casts.rs:34:9
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:29:46
|
-LL | transmute::<*const [i32], *const [u16]>(slice_ptr)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]`
+LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]`
error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead
- --> $DIR/transmutes_expressible_as_ptr_casts.rs:42:9
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:35:50
|
-LL | transmute::<*const i32, usize>(ptr_i32)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize`
+LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize`
|
= note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings`
error: transmute from a reference to a pointer
- --> $DIR/transmutes_expressible_as_ptr_casts.rs:50:9
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:41:41
|
-LL | transmute::<&[i32; 4], *const [i32; 4]>(array_ref)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]`
+LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]`
error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead
- --> $DIR/transmutes_expressible_as_ptr_casts.rs:58:9
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:49:41
|
-LL | transmute::<fn(usize) -> u8, *const usize>(foo)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize`
+LL | let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize`
error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead
- --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:9
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:53:49
|
-LL | transmute::<fn(usize) -> u8, usize>(foo)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize`
+LL | let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize`
error: transmute from a reference to a pointer
- --> $DIR/transmutes_expressible_as_ptr_casts.rs:77:14
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:65:14
|
LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8`
#[macro_use]
extern crate macro_rules;
+use std::io;
+use std::task::Poll;
+
// Tests that a simple case works
// Should flag `Err(err)?`
pub fn basic_test() -> Result<i32, i32> {
}
Ok(0)
}
+
+pub fn poll_write(n: usize) -> Poll<io::Result<usize>> {
+ if n == 0 {
+ return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))
+ } else if n == 1 {
+ return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))
+ };
+
+ Poll::Ready(Ok(n))
+}
+
+pub fn poll_next(ready: bool) -> Poll<Option<io::Result<()>>> {
+ if !ready {
+ return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))
+ }
+
+ Poll::Ready(None)
+}
#[macro_use]
extern crate macro_rules;
+use std::io;
+use std::task::Poll;
+
// Tests that a simple case works
// Should flag `Err(err)?`
pub fn basic_test() -> Result<i32, i32> {
}
Ok(0)
}
+
+pub fn poll_write(n: usize) -> Poll<io::Result<usize>> {
+ if n == 0 {
+ Err(io::ErrorKind::WriteZero)?
+ } else if n == 1 {
+ Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))?
+ };
+
+ Poll::Ready(Ok(n))
+}
+
+pub fn poll_next(ready: bool) -> Poll<Option<io::Result<()>>> {
+ if !ready {
+ Err(io::ErrorKind::NotFound)?
+ }
+
+ Poll::Ready(None)
+}
error: returning an `Err(_)` with the `?` operator
- --> $DIR/try_err.rs:15:9
+ --> $DIR/try_err.rs:18:9
|
LL | Err(err)?;
| ^^^^^^^^^ help: try this: `return Err(err)`
| ^^^^^^^^^^^^^^^
error: returning an `Err(_)` with the `?` operator
- --> $DIR/try_err.rs:25:9
+ --> $DIR/try_err.rs:28:9
|
LL | Err(err)?;
| ^^^^^^^^^ help: try this: `return Err(err.into())`
error: returning an `Err(_)` with the `?` operator
- --> $DIR/try_err.rs:45:17
+ --> $DIR/try_err.rs:48:17
|
LL | Err(err)?;
| ^^^^^^^^^ help: try this: `return Err(err)`
error: returning an `Err(_)` with the `?` operator
- --> $DIR/try_err.rs:64:17
+ --> $DIR/try_err.rs:67:17
|
LL | Err(err)?;
| ^^^^^^^^^ help: try this: `return Err(err.into())`
error: returning an `Err(_)` with the `?` operator
- --> $DIR/try_err.rs:103:9
+ --> $DIR/try_err.rs:106:9
|
LL | Err(foo!())?;
| ^^^^^^^^^^^^ help: try this: `return Err(foo!())`
-error: aborting due to 5 previous errors
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:113:9
+ |
+LL | Err(io::ErrorKind::WriteZero)?
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))`
+
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:115:9
+ |
+LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))?
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))`
+
+error: returning an `Err(_)` with the `?` operator
+ --> $DIR/try_err.rs:123:9
+ |
+LL | Err(io::ErrorKind::NotFound)?
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))`
+
+error: aborting due to 8 previous errors
-error: Usage of unknown attribute
+error: usage of unknown attribute
--> $DIR/unknown_attribute.rs:1:11
|
LL | #[clippy::unknown]
-error: Creating a reference that is immediately dereferenced.
+error: creating a reference that is immediately dereferenced
--> $DIR/unnecessary_ref.rs:13:17
|
LL | let inner = (&outer).inner;
// run-rustfix
+#![allow(clippy::stable_sort_primitive)]
+
use std::cmp::Reverse;
fn unnecessary_sort_by() {
// run-rustfix
+#![allow(clippy::stable_sort_primitive)]
+
use std::cmp::Reverse;
fn unnecessary_sort_by() {
error: use Vec::sort here instead
- --> $DIR/unnecessary_sort_by.rs:12:5
+ --> $DIR/unnecessary_sort_by.rs:14:5
|
LL | vec.sort_by(|a, b| a.cmp(b));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()`
= note: `-D clippy::unnecessary-sort-by` implied by `-D warnings`
error: use Vec::sort here instead
- --> $DIR/unnecessary_sort_by.rs:13:5
+ --> $DIR/unnecessary_sort_by.rs:15:5
|
LL | vec.sort_unstable_by(|a, b| a.cmp(b));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()`
error: use Vec::sort_by_key here instead
- --> $DIR/unnecessary_sort_by.rs:14:5
+ --> $DIR/unnecessary_sort_by.rs:16:5
|
LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())`
error: use Vec::sort_by_key here instead
- --> $DIR/unnecessary_sort_by.rs:15:5
+ --> $DIR/unnecessary_sort_by.rs:17:5
|
LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&a| id(-a))`
error: use Vec::sort_by_key here instead
- --> $DIR/unnecessary_sort_by.rs:17:5
+ --> $DIR/unnecessary_sort_by.rs:19:5
|
LL | vec.sort_by(|a, b| b.cmp(a));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))`
error: use Vec::sort_by_key here instead
- --> $DIR/unnecessary_sort_by.rs:18:5
+ --> $DIR/unnecessary_sort_by.rs:20:5
|
LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))`
error: use Vec::sort_by_key here instead
- --> $DIR/unnecessary_sort_by.rs:19:5
+ --> $DIR/unnecessary_sort_by.rs:21:5
|
LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))`
-error: You matched a field with a wildcard pattern. Consider using `..` instead
+error: you matched a field with a wildcard pattern, consider using `..` instead
--> $DIR/unneeded_field_pattern.rs:14:15
|
LL | Foo { a: _, b: 0, .. } => {},
| ^^^^
|
= note: `-D clippy::unneeded-field-pattern` implied by `-D warnings`
- = help: Try with `Foo { b: 0, .. }`
+ = help: try with `Foo { b: 0, .. }`
-error: All the struct fields are matched to a wildcard pattern, consider using `..`.
+error: all the struct fields are matched to a wildcard pattern, consider using `..`
--> $DIR/unneeded_field_pattern.rs:16:9
|
LL | Foo { a: _, b: _, c: _ } => {},
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: Try with `Foo { .. }` instead
+ = help: try with `Foo { .. }` instead
error: aborting due to 2 previous errors
#[derive(Deserialize)]
pub struct F {}
+// Check that we honor the `allow` attribute on the ADT
+#[allow(clippy::unsafe_derive_deserialize)]
+#[derive(Deserialize)]
+pub struct G {}
+impl G {
+ pub fn unsafe_block(&self) {
+ unsafe {}
+ }
+}
+
fn main() {}