Auto merge of #75405 - flip1995:clippyup, r=Manishearth

Update Clippy

Biweekly Clippy update (2 days late, since I wanted to wait for https://github.com/rust-lang/rust/pull/75098)

r? @Manishearth
This commit is contained in:
bors 2020-08-12 00:34:19 +00:00
commit fdc2f879f1
127 changed files with 2700 additions and 619 deletions

View file

@ -6,13 +6,13 @@ document.
## Unreleased / In Rust Nightly
[c2c07fa...master](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master)
[c2c07fa...master](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...master)
## Rust 1.46
Current beta, release 2020-08-27
[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master)
[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
### New lints
@ -1454,6 +1454,7 @@ Released 2018-09-13
[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
@ -1615,6 +1616,7 @@ Released 2018-09-13
[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
@ -1686,6 +1688,7 @@ Released 2018-09-13
[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
@ -1701,6 +1704,7 @@ Released 2018-09-13
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
@ -1723,6 +1727,7 @@ Released 2018-09-13
[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool

View file

@ -28,11 +28,14 @@ All contributors are expected to follow the [Rust Code of Conduct].
## Getting started
High level approach:
**Note: If this is your first time contributing to Clippy, you should
first read the [Basics docs](doc/basics.md).**
### High level approach
1. Find something to fix/improve
2. Change code (likely some file in `clippy_lints/src/`)
3. Follow the instructions in the [docs for writing lints](doc/adding_lints.md) such as running the `setup-toolchain.sh` script
3. Follow the instructions in the [Basics docs](doc/basics.md) to get set up
4. Run `cargo test` in the root directory and wiggle code until it passes
5. Open a PR (also can be done after 2. if you run into problems)

View file

@ -68,10 +68,11 @@ fn inject_deps_into_manifest(
});
// format a new [dependencies]-block with the new deps we need to inject
let mut all_deps = String::from("[dependencies]\n");
let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
new_deps.for_each(|dep_line| {
all_deps.push_str(&dep_line);
});
all_deps.push_str("\n[dependencies]\n");
// replace "[dependencies]" with
// [dependencies]

View file

@ -1,6 +1,5 @@
//! checks for attributes
use crate::reexport::Name;
use crate::utils::{
first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_help,
span_lint_and_sugg, span_lint_and_then, without_block_comments,
@ -517,7 +516,7 @@ fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>
}
}
fn check_attrs(cx: &LateContext<'_>, span: Span, name: Name, attrs: &[Attribute]) {
fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
if span.from_expansion() {
return;
}
@ -606,7 +605,7 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::as
cx,
EMPTY_LINE_AFTER_OUTER_ATTR,
begin_of_attr_to_item,
"Found an empty line after an outer attribute. \
"found an empty line after an outer attribute. \
Perhaps you forgot to add a `!` to make it an inner attribute?",
);
}

View file

@ -82,8 +82,8 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
cx,
NAIVE_BYTECOUNT,
expr.span,
"You appear to be counting bytes the naive way",
"Consider using the bytecount crate",
"you appear to be counting bytes the naive way",
"consider using the bytecount crate",
format!("bytecount::count({}, {})",
snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
snippet_with_applicability(cx, needle.span, "..", &mut applicability)),

View file

@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
cx,
CHECKED_CONVERSIONS,
item.span,
"Checked cast can be simplified.",
"checked cast can be simplified",
"try",
format!("{}::try_from({}).is_ok()", to_type, snippet),
applicability,

View file

@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess {
cx,
DEFAULT_TRAIT_ACCESS,
expr.span,
&format!("Calling `{}` is more clear than this expression", replacement),
&format!("calling `{}` is more clear than this expression", replacement),
"try",
replacement,
Applicability::Unspecified, // First resolve the TODO above

View file

@ -1,6 +1,7 @@
use crate::utils::paths;
use crate::utils::{
is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then,
get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help,
span_lint_and_note, span_lint_and_then,
};
use if_chain::if_chain;
use rustc_hir::def_id::DefId;
@ -43,6 +44,57 @@ declare_clippy_lint! {
"deriving `Hash` but implementing `PartialEq` explicitly"
}
declare_clippy_lint! {
/// **What it does:** Checks for deriving `Ord` but implementing `PartialOrd`
/// explicitly or vice versa.
///
/// **Why is this bad?** The implementation of these traits must agree (for
/// example for use with `sort`) so its probably a bad idea to use a
/// default-generated `Ord` implementation with an explicitly defined
/// `PartialOrd`. In particular, the following must hold for any type
/// implementing `Ord`:
///
/// ```text
/// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
/// ```
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust,ignore
/// #[derive(Ord, PartialEq, Eq)]
/// struct Foo;
///
/// impl PartialOrd for Foo {
/// ...
/// }
/// ```
/// Use instead:
/// ```rust,ignore
/// #[derive(PartialEq, Eq)]
/// struct Foo;
///
/// impl PartialOrd for Foo {
/// fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
/// Some(self.cmp(other))
/// }
/// }
///
/// impl Ord for Foo {
/// ...
/// }
/// ```
/// or, if you don't need a custom ordering:
/// ```rust,ignore
/// #[derive(Ord, PartialOrd, PartialEq, Eq)]
/// struct Foo;
/// ```
pub DERIVE_ORD_XOR_PARTIAL_ORD,
correctness,
"deriving `Ord` but implementing `PartialOrd` explicitly"
}
declare_clippy_lint! {
/// **What it does:** Checks for explicit `Clone` implementations for `Copy`
/// types.
@ -103,7 +155,12 @@ declare_clippy_lint! {
"deriving `serde::Deserialize` on a type that has methods using `unsafe`"
}
declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, UNSAFE_DERIVE_DESERIALIZE]);
declare_lint_pass!(Derive => [
EXPL_IMPL_CLONE_ON_COPY,
DERIVE_HASH_XOR_EQ,
DERIVE_ORD_XOR_PARTIAL_ORD,
UNSAFE_DERIVE_DESERIALIZE
]);
impl<'tcx> LateLintPass<'tcx> for Derive {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@ -116,6 +173,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
let is_automatically_derived = is_automatically_derived(&*item.attrs);
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
if is_automatically_derived {
check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
@ -180,6 +238,60 @@ fn check_hash_peq<'tcx>(
}
}
/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
fn check_ord_partial_ord<'tcx>(
cx: &LateContext<'tcx>,
span: Span,
trait_ref: &TraitRef<'_>,
ty: Ty<'tcx>,
ord_is_automatically_derived: bool,
) {
if_chain! {
if let Some(ord_trait_def_id) = get_trait_def_id(cx, &paths::ORD);
if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait();
if let Some(def_id) = &trait_ref.trait_def_id();
if *def_id == ord_trait_def_id;
then {
// Look for the PartialOrd implementations for `ty`
cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
let partial_ord_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id));
if partial_ord_is_automatically_derived == ord_is_automatically_derived {
return;
}
let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
// Only care about `impl PartialOrd<Foo> for Foo`
// For `impl PartialOrd<B> for A, input_types is [A, B]
if trait_ref.substs.type_at(1) == ty {
let mess = if partial_ord_is_automatically_derived {
"you are implementing `Ord` explicitly but have derived `PartialOrd`"
} else {
"you are deriving `Ord` but have implemented `PartialOrd` explicitly"
};
span_lint_and_then(
cx,
DERIVE_ORD_XOR_PARTIAL_ORD,
span,
mess,
|diag| {
if let Some(local_def_id) = impl_id.as_local() {
let hir_id = cx.tcx.hir().as_local_hir_id(local_def_id);
diag.span_note(
cx.tcx.hir().span(hir_id),
"`PartialOrd` implemented here"
);
}
}
);
}
});
}
}
}
/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
if match_path(&trait_ref.path, &paths::CLONE_TRAIT) {
@ -242,7 +354,9 @@ fn check_unsafe_derive_deserialize<'tcx>(
if_chain! {
if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE);
if let ty::Adt(def, _) = ty.kind;
if def.did.is_local();
if let Some(local_def_id) = def.did.as_local();
let adt_hir_id = cx.tcx.hir().as_local_hir_id(local_def_id);
if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
if cx.tcx.inherent_impls(def.did)
.iter()
.map(|imp_did| item_from_def_id(cx, *imp_did))

View file

@ -60,7 +60,7 @@ impl<'tcx> DoubleComparisons {
cx,
DOUBLE_COMPARISONS,
span,
"This binary expression can be simplified",
"this binary expression can be simplified",
"try",
sugg,
applicability,

View file

@ -45,15 +45,12 @@ impl EarlyLintPass for DoubleParens {
return;
}
let msg: &str = "consider removing unnecessary double parentheses";
match expr.kind {
ExprKind::Paren(ref in_paren) => match in_paren.kind {
ExprKind::Paren(_) | ExprKind::Tup(_) => {
span_lint(
cx,
DOUBLE_PARENS,
expr.span,
"Consider removing unnecessary double parentheses",
);
span_lint(cx, DOUBLE_PARENS, expr.span, &msg);
},
_ => {},
},
@ -61,12 +58,7 @@ impl EarlyLintPass for DoubleParens {
if params.len() == 1 {
let param = &params[0];
if let ExprKind::Paren(_) = param.kind {
span_lint(
cx,
DOUBLE_PARENS,
param.span,
"Consider removing unnecessary double parentheses",
);
span_lint(cx, DOUBLE_PARENS, param.span, &msg);
}
}
},
@ -74,12 +66,7 @@ impl EarlyLintPass for DoubleParens {
if params.len() == 2 {
let param = &params[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);
}
}
},

View file

@ -33,11 +33,11 @@ declare_clippy_lint! {
/// ```
pub DROP_BOUNDS,
correctness,
"Bounds of the form `T: Drop` are useless"
"bounds of the form `T: Drop` are useless"
}
const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \
Use `std::mem::needs_drop` to detect if a type has drop glue.";
const DROP_BOUNDS_SUMMARY: &str = "bounds of the form `T: Drop` are useless, \
use `std::mem::needs_drop` to detect if a type has drop glue";
declare_lint_pass!(DropBounds => [DROP_BOUNDS]);

View file

@ -294,7 +294,8 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
let body = cx.tcx.hir().body(eid);
Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id);
if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) {
if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs)
{
check_must_use_candidate(
cx,
&sig.decl,
@ -373,7 +374,7 @@ impl<'tcx> Functions {
}
if line_count > self.max_lines {
span_lint(cx, TOO_MANY_LINES, span, "This function has a large number of lines.")
span_lint(cx, TOO_MANY_LINES, span, "this function has a large number of lines")
}
}

View file

@ -250,6 +250,7 @@ mod mut_mut;
mod mut_reference;
mod mutable_debug_assertion;
mod mutex_atomic;
mod needless_arbitrary_self_type;
mod needless_bool;
mod needless_borrow;
mod needless_borrowed_ref;
@ -288,6 +289,7 @@ mod serde_api;
mod shadow;
mod single_component_path_imports;
mod slow_vector_initialization;
mod stable_sort_primitive;
mod strings;
mod suspicious_trait_impl;
mod swap;
@ -322,10 +324,6 @@ mod zero_div_zero;
pub use crate::utils::conf::Conf;
mod reexport {
pub use rustc_span::Symbol as Name;
}
/// Register all pre expansion lints
///
/// Pre-expansion lints run before any macro expansion has happened.
@ -513,6 +511,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&default_trait_access::DEFAULT_TRAIT_ACCESS,
&dereference::EXPLICIT_DEREF_METHODS,
&derive::DERIVE_HASH_XOR_EQ,
&derive::DERIVE_ORD_XOR_PARTIAL_ORD,
&derive::EXPL_IMPL_CLONE_ON_COPY,
&derive::UNSAFE_DERIVE_DESERIALIZE,
&doc::DOC_MARKDOWN,
@ -610,6 +609,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&loops::NEEDLESS_COLLECT,
&loops::NEEDLESS_RANGE_LOOP,
&loops::NEVER_LOOP,
&loops::SAME_ITEM_PUSH,
&loops::WHILE_IMMUTABLE_CONDITION,
&loops::WHILE_LET_LOOP,
&loops::WHILE_LET_ON_ITERATOR,
@ -719,6 +719,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
&mutex_atomic::MUTEX_ATOMIC,
&mutex_atomic::MUTEX_INTEGER,
&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
&needless_bool::BOOL_COMPARISON,
&needless_bool::NEEDLESS_BOOL,
&needless_borrow::NEEDLESS_BORROW,
@ -776,6 +777,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&shadow::SHADOW_UNRELATED,
&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
&stable_sort_primitive::STABLE_SORT_PRIMITIVE,
&strings::STRING_ADD,
&strings::STRING_ADD_ASSIGN,
&strings::STRING_LIT_AS_BYTES,
@ -786,6 +788,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
&temporary_assignment::TEMPORARY_ASSIGNMENT,
&to_digit_is_some::TO_DIGIT_IS_SOME,
&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
&trait_bounds::TYPE_REPETITION_IN_BOUNDS,
&transmute::CROSSPOINTER_TRANSMUTE,
&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
@ -1028,6 +1031,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
store.register_early_pass(|| box precedence::Precedence);
store.register_early_pass(|| box needless_continue::NeedlessContinue);
store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType);
store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes);
store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
@ -1079,6 +1083,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box macro_use::MacroUseImports::default());
store.register_late_pass(|| box map_identity::MapIdentity);
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
store.register_late_pass(|| box repeat_once::RepeatOnce);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
@ -1175,6 +1180,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&ranges::RANGE_PLUS_ONE),
LintId::of(&shadow::SHADOW_UNRELATED),
LintId::of(&strings::STRING_ADD_ASSIGN),
LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF),
LintId::of(&types::CAST_LOSSLESS),
@ -1231,6 +1237,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&copies::IFS_SAME_COND),
LintId::of(&copies::IF_SAME_THEN_ELSE),
LintId::of(&derive::DERIVE_HASH_XOR_EQ),
LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD),
LintId::of(&doc::MISSING_SAFETY_DOC),
LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),
LintId::of(&double_comparison::DOUBLE_COMPARISONS),
@ -1293,6 +1300,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&loops::NEEDLESS_COLLECT),
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
LintId::of(&loops::NEVER_LOOP),
LintId::of(&loops::SAME_ITEM_PUSH),
LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
LintId::of(&loops::WHILE_LET_LOOP),
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
@ -1369,6 +1377,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&mut_key::MUTABLE_KEY_TYPE),
LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(&mutex_atomic::MUTEX_ATOMIC),
LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
LintId::of(&needless_bool::BOOL_COMPARISON),
LintId::of(&needless_bool::NEEDLESS_BOOL),
LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
@ -1409,6 +1418,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&serde_api::SERDE_API_MISUSE),
LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(&strings::STRING_LIT_AS_BYTES),
LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
@ -1495,6 +1505,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&loops::EMPTY_LOOP),
LintId::of(&loops::FOR_KV_MAP),
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
LintId::of(&loops::SAME_ITEM_PUSH),
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
LintId::of(&main_recursion::MAIN_RECURSION),
LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
@ -1600,6 +1611,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&misc::SHORT_CIRCUIT_STATEMENT),
LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN),
LintId::of(&misc_early::ZERO_PREFIXED_LITERAL),
LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
LintId::of(&needless_bool::BOOL_COMPARISON),
LintId::of(&needless_bool::NEEDLESS_BOOL),
LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
@ -1651,6 +1663,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&copies::IFS_SAME_COND),
LintId::of(&copies::IF_SAME_THEN_ELSE),
LintId::of(&derive::DERIVE_HASH_XOR_EQ),
LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD),
LintId::of(&drop_bounds::DROP_BOUNDS),
LintId::of(&drop_forget_ref::DROP_COPY),
LintId::of(&drop_forget_ref::DROP_REF),
@ -1726,6 +1739,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&mutex_atomic::MUTEX_ATOMIC),
LintId::of(&redundant_clone::REDUNDANT_CLONE),
LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(&types::BOX_VEC),
LintId::of(&types::REDUNDANT_ALLOCATION),
LintId::of(&vec::USELESS_VEC),

View file

@ -13,9 +13,8 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::kw;
use rustc_span::symbol::{kw, Symbol};
use crate::reexport::Name;
use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method};
declare_clippy_lint! {
@ -113,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
enum RefLt {
Unnamed,
Static,
Named(Name),
Named(Symbol),
}
fn check_fn_inner<'tcx>(
@ -456,7 +455,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
}
struct LifetimeChecker {
map: FxHashMap<Name, Span>,
map: FxHashMap<Symbol, Span>,
}
impl<'tcx> Visitor<'tcx> for LifetimeChecker {

View file

@ -1,14 +1,14 @@
use crate::consts::constant;
use crate::reexport::Name;
use crate::utils::paths;
use crate::utils::sugg::Sugg;
use crate::utils::usage::{is_unused, mutated_variables};
use crate::utils::{
get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var,
multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help,
span_lint_and_sugg, span_lint_and_then, SpanlessEq,
is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability,
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
SpanlessEq,
};
use crate::utils::{is_type_diagnostic_item, qpath_res, sugg};
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@ -17,7 +17,7 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
use rustc_hir::{
def_id, BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, InlineAsmOperand,
LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind,
Local, LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind,
};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -27,7 +27,7 @@ use rustc_middle::middle::region;
use rustc_middle::ty::{self, Ty, TyS};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use std::iter::{once, Iterator};
use std::mem;
@ -420,6 +420,39 @@ declare_clippy_lint! {
"variables used within while expression are not mutated in the body"
}
declare_clippy_lint! {
/// **What it does:** Checks whether a for loop is being used to push a constant
/// value into a Vec.
///
/// **Why is this bad?** This kind of operation can be expressed more succinctly with
/// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also
/// have better performance.
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// let item1 = 2;
/// let item2 = 3;
/// let mut vec: Vec<u8> = Vec::new();
/// for _ in 0..20 {
/// vec.push(item1);
/// }
/// for _ in 0..30 {
/// vec.push(item2);
/// }
/// ```
/// could be written as
/// ```rust
/// let item1 = 2;
/// let item2 = 3;
/// let mut vec: Vec<u8> = vec![item1; 20];
/// vec.resize(20 + 30, item2);
/// ```
pub SAME_ITEM_PUSH,
style,
"the same item is pushed inside of a for loop"
}
declare_lint_pass!(Loops => [
MANUAL_MEMCPY,
NEEDLESS_RANGE_LOOP,
@ -436,6 +469,7 @@ declare_lint_pass!(Loops => [
NEVER_LOOP,
MUT_RANGE_BOUND,
WHILE_IMMUTABLE_CONDITION,
SAME_ITEM_PUSH,
]);
impl<'tcx> LateLintPass<'tcx> for Loops {
@ -741,6 +775,7 @@ fn check_for_loop<'tcx>(
check_for_loop_over_map_kv(cx, pat, arg, body, expr);
check_for_mut_range_bound(cx, arg, body);
detect_manual_memcpy(cx, pat, arg, body, expr);
detect_same_item_push(cx, pat, arg, body, expr);
}
fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool {
@ -1017,6 +1052,117 @@ fn detect_manual_memcpy<'tcx>(
}
}
// Scans the body of the for loop and determines whether lint should be given
struct SameItemPushVisitor<'a, 'tcx> {
should_lint: bool,
// this field holds the last vec push operation visited, which should be the only push seen
vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>,
cx: &'a LateContext<'tcx>,
}
impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
match &expr.kind {
// Non-determinism may occur ... don't give a lint
ExprKind::Loop(_, _, _) | ExprKind::Match(_, _, _) => self.should_lint = false,
ExprKind::Block(block, _) => self.visit_block(block),
_ => {},
}
}
fn visit_block(&mut self, b: &'tcx Block<'_>) {
for stmt in b.stmts.iter() {
self.visit_stmt(stmt);
}
}
fn visit_stmt(&mut self, s: &'tcx Stmt<'_>) {
let vec_push_option = get_vec_push(self.cx, s);
if vec_push_option.is_none() {
// Current statement is not a push so visit inside
match &s.kind {
StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(&expr),
_ => {},
}
} else {
// Current statement is a push ...check whether another
// push had been previously done
if self.vec_push.is_none() {
self.vec_push = vec_push_option;
} else {
// There are multiple pushes ... don't lint
self.should_lint = false;
}
}
}
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
}
// Given some statement, determine if that statement is a push on a Vec. If it is, return
// the Vec being pushed into and the item being pushed
fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
if_chain! {
// Extract method being called
if let StmtKind::Semi(semi_stmt) = &stmt.kind;
if let ExprKind::MethodCall(path, _, args, _) = &semi_stmt.kind;
// Figure out the parameters for the method call
if let Some(self_expr) = args.get(0);
if let Some(pushed_item) = args.get(1);
// Check that the method being called is push() on a Vec
if match_type(cx, cx.typeck_results().expr_ty(self_expr), &paths::VEC);
if path.ident.name.as_str() == "push";
then {
return Some((self_expr, pushed_item))
}
}
None
}
/// Detects for loop pushing the same item into a Vec
fn detect_same_item_push<'tcx>(
cx: &LateContext<'tcx>,
pat: &'tcx Pat<'_>,
_: &'tcx Expr<'_>,
body: &'tcx Expr<'_>,
_: &'tcx Expr<'_>,
) {
// Determine whether it is safe to lint the body
let mut same_item_push_visitor = SameItemPushVisitor {
should_lint: true,
vec_push: None,
cx,
};
walk_expr(&mut same_item_push_visitor, body);
if same_item_push_visitor.should_lint {
if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push {
// Make sure that the push does not involve possibly mutating values
if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
if let PatKind::Wild = pat.kind {
let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
span_lint_and_help(
cx,
SAME_ITEM_PUSH,
vec.span,
"it looks like the same item is being pushed into this Vec",
None,
&format!(
"try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
item_str, vec_str, item_str
),
)
}
}
}
}
}
/// Checks for looping over a range and then indexing a sequence with it.
/// The iteratee must be a range literal.
#[allow(clippy::too_many_lines)]
@ -1184,7 +1330,7 @@ fn check_for_loop_range<'tcx>(
}
}
fn is_len_call(expr: &Expr<'_>, var: Name) -> bool {
fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
if_chain! {
if let ExprKind::MethodCall(ref method, _, ref len_args, _) = expr.kind;
if len_args.len() == 1;
@ -1640,15 +1786,15 @@ struct VarVisitor<'a, 'tcx> {
/// var name to look for as index
var: HirId,
/// indexed variables that are used mutably
indexed_mut: FxHashSet<Name>,
indexed_mut: FxHashSet<Symbol>,
/// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
indexed_indirectly: FxHashMap<Name, Option<region::Scope>>,
indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>,
/// subset of `indexed` of vars that are indexed directly: `v[i]`
/// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
indexed_directly: FxHashMap<Name, (Option<region::Scope>, Ty<'tcx>)>,
indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
/// Any names that are used outside an index operation.
/// Used to detect things like `&mut vec` used together with `vec[i]`
referenced: FxHashSet<Name>,
referenced: FxHashSet<Symbol>,
/// has the loop variable been used in expressions other than the index of
/// an index op?
nonindex: bool,
@ -2004,7 +2150,7 @@ struct InitializeVisitor<'a, 'tcx> {
end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here.
var_id: HirId,
state: VarState,
name: Option<Name>,
name: Option<Symbol>,
depth: u32, // depth of conditional expressions
past_loop: bool,
}
@ -2167,7 +2313,7 @@ use self::Nesting::{LookFurther, RuledOut, Unknown};
struct LoopNestVisitor {
hir_id: HirId,
iterator: Name,
iterator: Symbol,
nesting: Nesting,
}
@ -2218,7 +2364,7 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
}
}
fn path_name(e: &Expr<'_>) -> Option<Name> {
fn path_name(e: &Expr<'_>) -> Option<Symbol> {
if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind {
let segments = &path.segments;
if segments.len() == 1 {
@ -2358,6 +2504,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> {
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
check_needless_collect_direct_usage(expr, cx);
check_needless_collect_indirect_usage(expr, cx);
}
fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
if_chain! {
if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind;
@ -2371,7 +2521,7 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
match_type(cx, ty, &paths::BTREEMAP) ||
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {
if method.ident.name == sym!(len) {
let span = shorten_span(expr, sym!(collect));
let span = shorten_needless_collect_span(expr);
span_lint_and_sugg(
cx,
NEEDLESS_COLLECT,
@ -2383,20 +2533,20 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
);
}
if method.ident.name == sym!(is_empty) {
let span = shorten_span(expr, sym!(iter));
let span = shorten_needless_collect_span(expr);
span_lint_and_sugg(
cx,
NEEDLESS_COLLECT,
span,
NEEDLESS_COLLECT_MSG,
"replace with",
"get(0).is_none()".to_string(),
"next().is_none()".to_string(),
Applicability::MachineApplicable,
);
}
if method.ident.name == sym!(contains) {
let contains_arg = snippet(cx, args[1].span, "??");
let span = shorten_span(expr, sym!(collect));
let span = shorten_needless_collect_span(expr);
span_lint_and_then(
cx,
NEEDLESS_COLLECT,
@ -2425,13 +2575,164 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
}
}
fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span {
let mut current_expr = expr;
while let ExprKind::MethodCall(ref path, ref span, ref args, _) = current_expr.kind {
if path.ident.name == target_fn_name {
fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
if let ExprKind::Block(ref block, _) = expr.kind {
for ref stmt in block.stmts {
if_chain! {
if let StmtKind::Local(
Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. },
init: Some(ref init_expr), .. }
) = stmt.kind;
if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind;
if method_name.ident.name == sym!(collect) && match_trait_method(cx, &init_expr, &paths::ITERATOR);
if let Some(ref generic_args) = method_name.args;
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
if let ty = cx.typeck_results().node_type(ty.hir_id);
if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
match_type(cx, ty, &paths::LINKED_LIST);
if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
if iter_calls.len() == 1;
then {
// Suggest replacing iter_call with iter_replacement, and removing stmt
let iter_call = &iter_calls[0];
span_lint_and_then(
cx,
NEEDLESS_COLLECT,
stmt.span.until(iter_call.span),
NEEDLESS_COLLECT_MSG,
|diag| {
let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx));
diag.multipart_suggestion(
iter_call.get_suggestion_text(),
vec![
(stmt.span, String::new()),
(iter_call.span, iter_replacement)
],
Applicability::MachineApplicable,// MaybeIncorrect,
).emit();
},
);
}
}
}
}
}
struct IterFunction {
func: IterFunctionKind,
span: Span,
}
impl IterFunction {
fn get_iter_method(&self, cx: &LateContext<'_>) -> String {
match &self.func {
IterFunctionKind::IntoIter => String::new(),
IterFunctionKind::Len => String::from(".count()"),
IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")),
}
}
fn get_suggestion_text(&self) -> &'static str {
match &self.func {
IterFunctionKind::IntoIter => {
"Use the original Iterator instead of collecting it and then producing a new one"
},
IterFunctionKind::Len => {
"Take the original Iterator's count instead of collecting it and finding the length"
},
IterFunctionKind::IsEmpty => {
"Check if the original Iterator has anything instead of collecting it and seeing if it's empty"
},
IterFunctionKind::Contains(_) => {
"Check if the original Iterator contains an element instead of collecting then checking"
},
}
}
}
enum IterFunctionKind {
IntoIter,
Len,
IsEmpty,
Contains(Span),
}
struct IterFunctionVisitor {
uses: Vec<IterFunction>,
seen_other: bool,
target: Ident,
}
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
// Check function calls on our collection
if_chain! {
if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind;
if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0);
if let &[name] = &path.segments;
if name.ident == self.target;
then {
let len = sym!(len);
let is_empty = sym!(is_empty);
let contains = sym!(contains);
match method_name.ident.name {
sym::into_iter => self.uses.push(
IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }
),
name if name == len => self.uses.push(
IterFunction { func: IterFunctionKind::Len, span: expr.span }
),
name if name == is_empty => self.uses.push(
IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }
),
name if name == contains => self.uses.push(
IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }
),
_ => self.seen_other = true,
}
return
}
}
// Check if the collection is used for anything else
if_chain! {
if let Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. } = expr;
if let &[name] = &path.segments;
if name.ident == self.target;
then {
self.seen_other = true;
} else {
walk_expr(self, expr);
}
}
}
type Map = Map<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
}
/// Detect the occurences of calls to `iter` or `into_iter` for the
/// given identifier
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
let mut visitor = IterFunctionVisitor {
uses: Vec::new(),
target: identifier,
seen_other: false,
};
visitor.visit_block(block);
if visitor.seen_other {
None
} else {
Some(visitor.uses)
}
}
fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span {
if_chain! {
if let ExprKind::MethodCall(.., args, _) = &expr.kind;
if let ExprKind::MethodCall(_, span, ..) = &args[0].kind;
then {
return expr.span.with_lo(span.lo());
}
current_expr = &args[0];
}
unreachable!()
unreachable!();
}

View file

@ -4,8 +4,8 @@ use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync,
ItemKind, TraitRef, Ty, TyKind, TypeBindingKind,
AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId,
IsAsync, ItemKind, LifetimeName, TraitRef, Ty, TyKind, TypeBindingKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -27,8 +27,6 @@ declare_clippy_lint! {
/// ```
/// Use instead:
/// ```rust
/// use std::future::Future;
///
/// async fn foo() -> i32 { 42 }
/// ```
pub MANUAL_ASYNC_FN,
@ -53,8 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
if let IsAsync::NotAsync = header.asyncness;
// Check that this function returns `impl Future`
if let FnRetTy::Return(ret_ty) = decl.output;
if let Some(trait_ref) = future_trait_ref(cx, ret_ty);
if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty);
if let Some(output) = future_output_ty(trait_ref);
if captures_all_lifetimes(decl.inputs, &output_lifetimes);
// Check that the body of the function consists of one async block
if let ExprKind::Block(block, _) = body.value.kind;
if block.stmts.is_empty();
@ -97,16 +96,35 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
}
}
fn future_trait_ref<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> {
fn future_trait_ref<'tcx>(
cx: &LateContext<'tcx>,
ty: &'tcx Ty<'tcx>,
) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> {
if_chain! {
if let TyKind::OpaqueDef(item_id, _) = ty.kind;
if let TyKind::OpaqueDef(item_id, bounds) = ty.kind;
let item = cx.tcx.hir().item(item_id.id);
if let ItemKind::OpaqueTy(opaque) = &item.kind;
if opaque.bounds.len() == 1;
if let GenericBound::Trait(poly, _) = &opaque.bounds[0];
if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
if let GenericBound::Trait(poly, _) = bound {
Some(&poly.trait_ref)
} else {
None
}
});
if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
then {
return Some(&poly.trait_ref);
let output_lifetimes = bounds
.iter()
.filter_map(|bound| {
if let GenericArg::Lifetime(lt) = bound {
Some(lt.name)
} else {
None
}
})
.collect();
return Some((trait_ref, output_lifetimes));
}
}
@ -129,6 +147,29 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t
None
}
fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) -> bool {
let input_lifetimes: Vec<LifetimeName> = inputs
.iter()
.filter_map(|ty| {
if let TyKind::Rptr(lt, _) = ty.kind {
Some(lt.name)
} else {
None
}
})
.collect();
// The lint should trigger in one of these cases:
// - There are no input lifetimes
// - There's only one output lifetime bound using `+ '_`
// - All input lifetimes are explicitly bound to the output
input_lifetimes.is_empty()
|| (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore))
|| input_lifetimes
.iter()
.all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt))
}
fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
if_chain! {
if let Some(block_expr) = block.expr;

View file

@ -1052,8 +1052,7 @@ declare_clippy_lint! {
///
/// **Why is this bad?** Readability.
///
/// **Known problems:** False positive in pattern guards. Will be resolved once
/// non-lexical lifetimes are stable.
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
@ -1408,7 +1407,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true),
["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]),
["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]),
["next", "skip"] => lint_iter_skip_next(cx, expr),
["next", "skip"] => lint_iter_skip_next(cx, expr, arg_lists[1]),
["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]),
["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]),
["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]),
@ -2433,17 +2432,21 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args:
);
}
fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) {
// lint if caller of skip is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
span_lint_and_help(
cx,
ITER_SKIP_NEXT,
expr.span,
"called `skip(x).next()` on an iterator",
None,
"this is more succinctly expressed by calling `nth(x)`",
);
if let [caller, n] = skip_args {
let hint = format!(".nth({})", snippet(cx, n.span, ".."));
span_lint_and_sugg(
cx,
ITER_SKIP_NEXT,
expr.span.trim_start(caller.span).unwrap(),
"called `skip(x).next()` on an iterator",
"use `nth` instead",
hint,
Applicability::MachineApplicable,
);
}
}
}
@ -2565,17 +2568,34 @@ fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Ex
fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) {
// lint if caller of `.map().flatten()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
let msg = "called `map(..).flatten()` on an `Iterator`. \
This is more succinctly expressed by calling `.flat_map(..)`";
let self_snippet = snippet(cx, map_args[0].span, "..");
let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]);
let is_map_to_option = match map_closure_ty.kind {
ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
let map_closure_sig = match map_closure_ty.kind {
ty::Closure(_, substs) => substs.as_closure().sig(),
_ => map_closure_ty.fn_sig(cx.tcx),
};
let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&map_closure_sig.output());
is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type))
},
_ => false,
};
let method_to_use = if is_map_to_option {
// `(...).map(...)` has type `impl Iterator<Item=Option<...>>
"filter_map"
} else {
// `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
"flat_map"
};
let func_snippet = snippet(cx, map_args[1].span, "..");
let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet);
let hint = format!(".{0}({1})", method_to_use, func_snippet);
span_lint_and_sugg(
cx,
MAP_FLATTEN,
expr.span,
msg,
"try using `flat_map` instead",
expr.span.with_lo(map_args[0].span.hi()),
"called `map(..).flatten()` on an `Iterator`",
&format!("try using `{}` instead", method_to_use),
hint,
Applicability::MachineApplicable,
);
@ -2583,16 +2603,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
// lint if caller of `.map().flatten()` is an Option
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) {
let msg = "called `map(..).flatten()` on an `Option`. \
This is more succinctly expressed by calling `.and_then(..)`";
let self_snippet = snippet(cx, map_args[0].span, "..");
let func_snippet = snippet(cx, map_args[1].span, "..");
let hint = format!("{0}.and_then({1})", self_snippet, func_snippet);
let hint = format!(".and_then({})", func_snippet);
span_lint_and_sugg(
cx,
MAP_FLATTEN,
expr.span,
msg,
expr.span.with_lo(map_args[0].span.hi()),
"called `map(..).flatten()` on an `Option`",
"try using `and_then` instead",
hint,
Applicability::MachineApplicable,

View file

@ -1,5 +1,6 @@
use crate::consts::{constant_simple, Constant};
use crate::utils::{match_def_path, paths, span_lint};
use crate::utils::{match_def_path, match_trait_method, paths, span_lint};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -18,6 +19,10 @@ declare_clippy_lint! {
/// ```ignore
/// min(0, max(100, x))
/// ```
/// or
/// ```ignore
/// x.max(100).min(0)
/// ```
/// It will always be equal to `0`. Probably the author meant to clamp the value
/// between 0 and 100, but has erroneously swapped `min` and `max`.
pub MIN_MAX,
@ -60,25 +65,43 @@ enum MinMax {
}
fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
if let ExprKind::Call(ref path, ref args) = expr.kind {
if let ExprKind::Path(ref qpath) = path.kind {
cx.typeck_results()
.qpath_res(qpath, path.hir_id)
.opt_def_id()
.and_then(|def_id| {
if match_def_path(cx, def_id, &paths::CMP_MIN) {
fetch_const(cx, args, MinMax::Min)
} else if match_def_path(cx, def_id, &paths::CMP_MAX) {
match expr.kind {
ExprKind::Call(ref path, ref args) => {
if let ExprKind::Path(ref qpath) = path.kind {
cx.typeck_results()
.qpath_res(qpath, path.hir_id)
.opt_def_id()
.and_then(|def_id| {
if match_def_path(cx, def_id, &paths::CMP_MIN) {
fetch_const(cx, args, MinMax::Min)
} else if match_def_path(cx, def_id, &paths::CMP_MAX) {
fetch_const(cx, args, MinMax::Max)
} else {
None
}
})
} else {
None
}
},
ExprKind::MethodCall(ref path, _, ref args, _) => {
if_chain! {
if let [obj, _] = args;
if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD);
then {
if path.ident.as_str() == sym!(max).as_str() {
fetch_const(cx, args, MinMax::Max)
} else if path.ident.as_str() == sym!(min).as_str() {
fetch_const(cx, args, MinMax::Min)
} else {
None
}
})
} else {
None
}
} else {
None
} else {
None
}
}
},
_ => None,
}
}

View file

@ -271,7 +271,7 @@ impl EarlyLintPass for MiscEarlyLints {
cx,
BUILTIN_TYPE_SHADOW,
param.ident.span,
&format!("This generic shadows the built-in type `{}`", name),
&format!("this generic shadows the built-in type `{}`", name),
);
}
}
@ -298,9 +298,9 @@ impl EarlyLintPass for MiscEarlyLints {
cx,
UNNEEDED_FIELD_PATTERN,
pat.span,
"All the struct fields are matched to a wildcard pattern, consider using `..`.",
"all the struct fields are matched to a wildcard pattern, consider using `..`",
None,
&format!("Try with `{} {{ .. }}` instead", type_name),
&format!("try with `{} {{ .. }}` instead", type_name),
);
return;
}
@ -313,7 +313,7 @@ impl EarlyLintPass for MiscEarlyLints {
cx,
UNNEEDED_FIELD_PATTERN,
field.span,
"You matched a field with a wildcard pattern. Consider using `..` instead",
"you matched a field with a wildcard pattern, consider using `..` instead",
);
} else {
let mut normal = vec![];
@ -333,10 +333,10 @@ impl EarlyLintPass for MiscEarlyLints {
cx,
UNNEEDED_FIELD_PATTERN,
field.span,
"You matched a field with a wildcard pattern. Consider using `..` \
"you matched a field with a wildcard pattern, consider using `..` \
instead",
None,
&format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
&format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
);
}
}

View file

@ -0,0 +1,118 @@
use crate::utils::span_lint_and_sugg;
use if_chain::if_chain;
use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::kw;
use rustc_span::Span;
declare_clippy_lint! {
/// **What it does:** The lint checks for `self` in fn parameters that
/// specify the `Self`-type explicitly
/// **Why is this bad?** Increases the amount and decreases the readability of code
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// enum ValType {
/// I32,
/// I64,
/// F32,
/// F64,
/// }
///
/// impl ValType {
/// pub fn bytes(self: Self) -> usize {
/// match self {
/// Self::I32 | Self::F32 => 4,
/// Self::I64 | Self::F64 => 8,
/// }
/// }
/// }
/// ```
///
/// Could be rewritten as
///
/// ```rust
/// enum ValType {
/// I32,
/// I64,
/// F32,
/// F64,
/// }
///
/// impl ValType {
/// pub fn bytes(self) -> usize {
/// match self {
/// Self::I32 | Self::F32 => 4,
/// Self::I64 | Self::F64 => 8,
/// }
/// }
/// }
/// ```
pub NEEDLESS_ARBITRARY_SELF_TYPE,
complexity,
"type of `self` parameter is already by default `Self`"
}
declare_lint_pass!(NeedlessArbitrarySelfType => [NEEDLESS_ARBITRARY_SELF_TYPE]);
enum Mode {
Ref(Option<Lifetime>),
Value,
}
fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) {
if_chain! {
if let [segment] = &path.segments[..];
if segment.ident.name == kw::SelfUpper;
then {
let self_param = match (binding_mode, mutbl) {
(Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
(Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name),
(Mode::Ref(None), Mutability::Not) => "&self".to_string(),
(Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name),
(Mode::Value, Mutability::Mut) => "mut self".to_string(),
(Mode::Value, Mutability::Not) => "self".to_string(),
};
span_lint_and_sugg(
cx,
NEEDLESS_ARBITRARY_SELF_TYPE,
span,
"the type of the `self` parameter does not need to be arbitrary",
"consider to change this parameter to",
self_param,
Applicability::MachineApplicable,
)
}
}
}
impl EarlyLintPass for NeedlessArbitrarySelfType {
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
if !p.is_self() {
return;
}
match &p.ty.kind {
TyKind::Path(None, path) => {
if let PatKind::Ident(BindingMode::ByValue(mutbl), _, _) = p.pat.kind {
check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl)
}
},
TyKind::Rptr(lifetime, mut_ty) => {
if_chain! {
if let TyKind::Path(None, path) = &mut_ty.ty.kind;
if let PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, _) = p.pat.kind;
then {
check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl)
}
}
},
_ => {},
}
}
}

View file

@ -243,7 +243,7 @@ fn check_comparison<'a, 'tcx>(
cx,
BOOL_COMPARISON,
e.span,
"This comparison might be written more concisely",
"this comparison might be written more concisely",
"try simplifying it as shown",
format!(
"{} != {}",

View file

@ -40,9 +40,8 @@ declare_clippy_lint! {
/// assert_eq!(v.len(), 42);
/// }
/// ```
///
/// should be
/// ```rust
/// // should be
/// fn foo(v: &[i32]) {
/// assert_eq!(v.len(), 42);
/// }
@ -159,26 +158,19 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
}
}
//
// * Exclude a type that is specifically bounded by `Borrow`.
// * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
// `serde::Serialize`)
let (implements_borrow_trait, all_borrowable_trait) = {
let preds = preds
.iter()
.filter(|t| t.self_ty() == ty)
.collect::<Vec<_>>();
let preds = preds.iter().filter(|t| t.self_ty() == ty).collect::<Vec<_>>();
(
preds.iter().any(|t| t.def_id() == borrow_trait),
!preds.is_empty() && {
let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty);
preds.iter().all(|t| {
let ty_params = t
.trait_ref
.substs
.iter()
.skip(1)
.collect::<Vec<_>>();
let ty_params = t.trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
implements_trait(cx, ty_empty_region, t.def_id(), &ty_params)
})
},

View file

@ -79,10 +79,10 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd {
cx,
NEG_CMP_OP_ON_PARTIAL_ORD,
expr.span,
"The use of negated comparison operators on partially ordered \
types produces code that is hard to read and refactor. Please \
"the use of negated comparison operators on partially ordered \
types produces code that is hard to read and refactor, please \
consider using the `partial_cmp` method instead, to make it \
clear that the two values could be incomparable."
clear that the two values could be incomparable"
)
}
}

View file

@ -47,7 +47,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
if let Constant::Int(1) = consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit));
if cx.typeck_results().expr_ty(exp).is_integral();
then {
span_lint(cx, NEG_MULTIPLY, span, "Negation by multiplying with `-1`");
span_lint(cx, NEG_MULTIPLY, span, "negation by multiplying with `-1`");
}
}
}

View file

@ -42,13 +42,13 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional {
if let BinOpKind::Lt = op.node {
if let BinOpKind::Add = op2.node {
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
"You are trying to use classic C overflow conditions that will fail in Rust.");
"you are trying to use classic C overflow conditions that will fail in Rust");
}
}
if let BinOpKind::Gt = op.node {
if let BinOpKind::Sub = op2.node {
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
"You are trying to use classic C underflow conditions that will fail in Rust.");
"you are trying to use classic C underflow conditions that will fail in Rust");
}
}
}
@ -67,13 +67,13 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional {
if let BinOpKind::Gt = op.node {
if let BinOpKind::Add = op2.node {
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
"You are trying to use classic C overflow conditions that will fail in Rust.");
"you are trying to use classic C overflow conditions that will fail in Rust");
}
}
if let BinOpKind::Lt = op.node {
if let BinOpKind::Sub = op2.node {
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
"You are trying to use classic C underflow conditions that will fail in Rust.");
"you are trying to use classic C underflow conditions that will fail in Rust");
}
}
}

View file

@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite {
cx,
PATH_BUF_PUSH_OVERWRITE,
lit.span,
"Calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
"try",
format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
Applicability::MachineApplicable,

View file

@ -145,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
cx,
CMP_NULL,
expr.span,
"Comparing with null is better expressed by the `.is_null()` method",
"comparing with null is better expressed by the `.is_null()` method",
);
}
}

View file

@ -160,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
span_lint(cx,
RANGE_ZIP_WITH_LEN,
expr.span,
&format!("It is more idiomatic to use `{}.iter().enumerate()`",
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
snippet(cx, iter_args[0].span, "_")));
}
}

View file

@ -86,13 +86,13 @@ impl EarlyLintPass for RedundantStaticLifetimes {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
if !item.span.from_expansion() {
if let ItemKind::Const(_, ref var_type, _) = item.kind {
self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime");
self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
// Don't check associated consts because `'static` cannot be elided on those (issue
// #2438)
}
if let ItemKind::Static(ref var_type, _, _) = item.kind {
self.visit_type(var_type, cx, "Statics have by default a `'static` lifetime");
self.visit_type(var_type, cx, "statics have by default a `'static` lifetime");
}
}
}

View file

@ -92,7 +92,7 @@ impl EarlyLintPass for RefInDeref {
cx,
REF_IN_DEREF,
object.span,
"Creating a reference that is immediately dereferenced.",
"creating a reference that is immediately dereferenced",
"try this",
snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(),
applicability,

View file

@ -1,4 +1,3 @@
use crate::reexport::Name;
use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
@ -10,6 +9,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol;
declare_clippy_lint! {
/// **What it does:** Checks for bindings that shadow other bindings already in
@ -123,7 +123,7 @@ fn check_fn<'tcx>(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Bo
check_expr(cx, &body.value, &mut bindings);
}
fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Name, Span)>) {
fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Symbol, Span)>) {
let len = bindings.len();
for stmt in block.stmts {
match stmt.kind {
@ -138,7 +138,7 @@ fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &
bindings.truncate(len);
}
fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Name, Span)>) {
fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Symbol, Span)>) {
if in_external_macro(cx.sess(), local.span) {
return;
}
@ -173,7 +173,7 @@ fn check_pat<'tcx>(
pat: &'tcx Pat<'_>,
init: Option<&'tcx Expr<'_>>,
span: Span,
bindings: &mut Vec<(Name, Span)>,
bindings: &mut Vec<(Symbol, Span)>,
) {
// TODO: match more stuff / destructuring
match pat.kind {
@ -254,7 +254,7 @@ fn check_pat<'tcx>(
fn lint_shadow<'tcx>(
cx: &LateContext<'tcx>,
name: Name,
name: Symbol,
span: Span,
pattern_span: Span,
init: Option<&'tcx Expr<'_>>,
@ -315,7 +315,7 @@ fn lint_shadow<'tcx>(
}
}
fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Name, Span)>) {
fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Symbol, Span)>) {
if in_external_macro(cx.sess(), expr.span) {
return;
}
@ -351,7 +351,7 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut
}
}
fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Name, Span)>) {
fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) {
match ty.kind {
TyKind::Slice(ref sty) => check_ty(cx, sty, bindings),
TyKind::Array(ref fty, ref anon_const) => {
@ -371,7 +371,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(
}
}
fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool {
fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Box(ref inner) | ExprKind::AddrOf(_, _, ref inner) => is_self_shadow(name, inner),
ExprKind::Block(ref block, _) => {
@ -383,6 +383,6 @@ fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool {
}
}
fn path_eq_name(name: Name, path: &Path<'_>) -> bool {
fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool {
!path.is_global() && path.segments.len() == 1 && path.segments[0].ident.as_str() == name.as_str()
}

View file

@ -0,0 +1,130 @@
use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:**
/// When sorting primitive values (integers, bools, chars, as well
/// as arrays, slices, and tuples of such items), it is better to
/// use an unstable sort than a stable sort.
///
/// **Why is this bad?**
/// Using a stable sort consumes more memory and cpu cycles. Because
/// values which compare equal are identical, preserving their
/// relative order (the guarantee that a stable sort provides) means
/// nothing, while the extra costs still apply.
///
/// **Known problems:**
/// None
///
/// **Example:**
///
/// ```rust
/// let mut vec = vec![2, 1, 3];
/// vec.sort();
/// ```
/// Use instead:
/// ```rust
/// let mut vec = vec![2, 1, 3];
/// vec.sort_unstable();
/// ```
pub STABLE_SORT_PRIMITIVE,
perf,
"use of sort() when sort_unstable() is equivalent"
}
declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]);
/// The three "kinds" of sorts
enum SortingKind {
Vanilla,
/* The other kinds of lint are currently commented out because they
* can map distinct values to equal ones. If the key function is
* provably one-to-one, or if the Cmp function conserves equality,
* then they could be linted on, but I don't know if we can check
* for that. */
/* ByKey,
* ByCmp, */
}
impl SortingKind {
/// The name of the stable version of this kind of sort
fn stable_name(&self) -> &str {
match self {
SortingKind::Vanilla => "sort",
/* SortingKind::ByKey => "sort_by_key",
* SortingKind::ByCmp => "sort_by", */
}
}
/// The name of the unstable version of this kind of sort
fn unstable_name(&self) -> &str {
match self {
SortingKind::Vanilla => "sort_unstable",
/* SortingKind::ByKey => "sort_unstable_by_key",
* SortingKind::ByCmp => "sort_unstable_by", */
}
}
/// Takes the name of a function call and returns the kind of sort
/// that corresponds to that function name (or None if it isn't)
fn from_stable_name(name: &str) -> Option<SortingKind> {
match name {
"sort" => Some(SortingKind::Vanilla),
// "sort_by" => Some(SortingKind::ByCmp),
// "sort_by_key" => Some(SortingKind::ByKey),
_ => None,
}
}
}
/// A detected instance of this lint
struct LintDetection {
slice_name: String,
method: SortingKind,
method_args: String,
}
fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> {
if_chain! {
if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind;
if let Some(slice) = &args.get(0);
if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str());
if is_slice_of_primitives(cx, slice);
then {
let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str })
} else {
None
}
}
}
impl LateLintPass<'_> for StableSortPrimitive {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if let Some(detection) = detect_stable_sort_primitive(cx, expr) {
span_lint_and_sugg(
cx,
STABLE_SORT_PRIMITIVE,
expr.span,
format!(
"Use {} instead of {}",
detection.method.unstable_name(),
detection.method.stable_name()
)
.as_str(),
"try",
format!(
"{}.{}({})",
detection.slice_name,
detection.method.unstable_name(),
detection.method_args
),
Applicability::MachineApplicable,
);
}
}
}

View file

@ -64,26 +64,22 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
| hir::BinOpKind::Gt => return,
_ => {},
}
// Check if the binary expression is part of another bi/unary expression
// or operator assignment as a child node
let mut parent_expr = cx.tcx.hir().get_parent_node(expr.hir_id);
while parent_expr != hir::CRATE_HIR_ID {
if let hir::Node::Expr(e) = cx.tcx.hir().get(parent_expr) {
match e.kind {
hir::ExprKind::Binary(..)
| hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _)
| hir::ExprKind::AssignOp(..) => return,
_ => {},
// Check for more than one binary operation in the implemented function
// Linting when multiple operations are involved can result in false positives
if_chain! {
let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id);
if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn);
if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind;
let body = cx.tcx.hir().body(body_id);
let mut visitor = BinaryExprVisitor { nb_binops: 0 };
then {
walk_expr(&mut visitor, &body.value);
if visitor.nb_binops > 1 {
return;
}
}
parent_expr = cx.tcx.hir().get_parent_node(parent_expr);
}
// as a parent node
let mut visitor = BinaryExprVisitor { in_binary_expr: false };
walk_expr(&mut visitor, expr);
if visitor.in_binary_expr {
return;
}
if let Some(impl_trait) = check_binop(
@ -102,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
cx,
SUSPICIOUS_ARITHMETIC_IMPL,
binop.span,
&format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait),
&format!("suspicious use of binary operator in `{}` impl", impl_trait),
);
}
@ -139,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
cx,
SUSPICIOUS_OP_ASSIGN_IMPL,
binop.span,
&format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait),
&format!("suspicious use of binary operator in `{}` impl", impl_trait),
);
}
}
@ -181,7 +177,7 @@ fn check_binop(
}
struct BinaryExprVisitor {
in_binary_expr: bool,
nb_binops: u32,
}
impl<'tcx> Visitor<'tcx> for BinaryExprVisitor {
@ -191,12 +187,13 @@ impl<'tcx> Visitor<'tcx> for BinaryExprVisitor {
match expr.kind {
hir::ExprKind::Binary(..)
| hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _)
| hir::ExprKind::AssignOp(..) => self.in_binary_expr = true,
| hir::ExprKind::AssignOp(..) => self.nb_binops += 1,
_ => {},
}
walk_expr(self, expr);
}
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}

View file

@ -2,9 +2,10 @@ use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::{GenericBound, Generics, WherePredicate};
use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
declare_clippy_lint! {
/// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
@ -29,6 +30,35 @@ declare_clippy_lint! {
"Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
}
declare_clippy_lint! {
/// **What it does:** Checks for cases where generics are being used and multiple
/// syntax specifications for trait bounds are used simultaneously.
///
/// **Why is this bad?** Duplicate bounds makes the code
/// less readable than specifing them only once.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
/// ```
///
/// Could be written as:
///
/// ```rust
/// fn func<T: Clone + Default>(arg: T) {}
/// ```
/// or
///
/// ```rust
/// fn func<T>(arg: T) where T: Clone + Default {}
/// ```
pub TRAIT_DUPLICATION_IN_BOUNDS,
pedantic,
"Check if the same trait bounds are specified twice during a function declaration"
}
#[derive(Copy, Clone)]
pub struct TraitBounds {
max_trait_bounds: u64,
@ -41,10 +71,25 @@ impl TraitBounds {
}
}
impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);
impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]);
impl<'tcx> LateLintPass<'tcx> for TraitBounds {
fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
self.check_type_repetition(cx, gen);
check_trait_bound_duplication(cx, gen);
}
}
fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> {
if let GenericBound::Trait(t, _) = bound {
Some((t.trait_ref.path.res, t.span))
} else {
None
}
}
impl TraitBounds {
fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
if in_macro(gen.span) {
return;
}
@ -101,3 +146,48 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
}
}
}
fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
if in_macro(gen.span) || gen.params.is_empty() || gen.where_clause.predicates.is_empty() {
return;
}
let mut map = FxHashMap::default();
for param in gen.params {
if let ParamName::Plain(ref ident) = param.name {
let res = param
.bounds
.iter()
.filter_map(get_trait_res_span_from_bound)
.collect::<Vec<_>>();
map.insert(*ident, res);
}
}
for predicate in gen.where_clause.predicates {
if_chain! {
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
if !in_macro(bound_predicate.span);
if let TyKind::Path(ref path) = bound_predicate.bounded_ty.kind;
if let QPath::Resolved(_, Path { ref segments, .. }) = path;
if let Some(segment) = segments.first();
if let Some(trait_resolutions_direct) = map.get(&segment.ident);
then {
for (res_where, _) in bound_predicate.bounds.iter().filter_map(get_trait_res_span_from_bound) {
if let Some((_, span_direct)) = trait_resolutions_direct
.iter()
.find(|(res_direct, _)| *res_direct == res_where) {
span_lint_and_help(
cx,
TRAIT_DUPLICATION_IN_BOUNDS,
*span_direct,
"this trait bound is already specified in the where clause",
None,
"consider removing this trait bound",
);
}
}
}
}
}
}

View file

@ -61,12 +61,14 @@ declare_clippy_lint! {
///
/// **Example:**
///
/// ```rust,ignore
/// core::intrinsics::transmute::<*const [i32], *const [u16]>(p)
/// ```rust
/// # let p: *const [i32] = &[];
/// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
/// ```
/// Use instead:
/// ```rust
/// p as *const [u16]
/// # let p: *const [i32] = &[];
/// p as *const [u16];
/// ```
pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
complexity,
@ -704,14 +706,14 @@ fn can_be_expressed_as_pointer_cast<'tcx>(
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
) -> bool {
use CastKind::*;
use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
matches!(
check_cast(cx, e, from_ty, to_ty),
Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast)
)
}
/// If a cast from from_ty to to_ty is valid, returns an Ok containing the kind of
/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of
/// the cast. In certain cases, including some invalid casts from array references
/// to pointers, this may cause additional errors to be emitted and/or ICE error
/// messages. This function will panic if that occurs.

View file

@ -1,10 +1,13 @@
use crate::utils::{match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg};
use crate::utils::{
is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite,
span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
use rustc_hir::{Expr, ExprKind, MatchSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::Ty;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
@ -65,19 +68,39 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
if let Some(ref err_arg) = err_args.get(0);
if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
if match_qpath(err_fun_path, &paths::RESULT_ERR);
if let Some(return_type) = find_err_return_type(cx, &expr.kind);
if let Some(return_ty) = find_return_type(cx, &expr.kind);
then {
let err_type = cx.typeck_results().expr_ty(err_arg);
let prefix;
let suffix;
let err_ty;
if let Some(ty) = result_error_type(cx, return_ty) {
prefix = "Err(";
suffix = ")";
err_ty = ty;
} else if let Some(ty) = poll_result_error_type(cx, return_ty) {
prefix = "Poll::Ready(Err(";
suffix = "))";
err_ty = ty;
} else if let Some(ty) = poll_option_result_error_type(cx, return_ty) {
prefix = "Poll::Ready(Some(Err(";
suffix = ")))";
err_ty = ty;
} else {
return;
};
let expr_err_ty = cx.typeck_results().expr_ty(err_arg);
let origin_snippet = if err_arg.span.from_expansion() {
snippet_with_macro_callsite(cx, err_arg.span, "_")
} else {
snippet(cx, err_arg.span, "_")
};
let suggestion = if err_type == return_type {
format!("return Err({})", origin_snippet)
let suggestion = if err_ty == expr_err_ty {
format!("return {}{}{}", prefix, origin_snippet, suffix)
} else {
format!("return Err({}.into())", origin_snippet)
format!("return {}{}.into(){}", prefix, origin_snippet, suffix)
};
span_lint_and_sugg(
@ -94,27 +117,68 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
}
}
// In order to determine whether to suggest `.into()` or not, we need to find the error type the
// function returns. To do that, we look for the From::from call (see tree above), and capture
// its output type.
fn find_err_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
/// Finds function return type by examining return expressions in match arms.
fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
if let ExprKind::Match(_, ref arms, MatchSource::TryDesugar) = expr {
arms.iter().find_map(|ty| find_err_return_type_arm(cx, ty))
} else {
None
for arm in arms.iter() {
if let ExprKind::Ret(Some(ref ret)) = arm.body.kind {
return Some(cx.typeck_results().expr_ty(ret));
}
}
}
None
}
// Check for From::from in one of the match arms.
fn find_err_return_type_arm<'tcx>(cx: &LateContext<'tcx>, arm: &'tcx Arm<'_>) -> Option<Ty<'tcx>> {
/// Extracts the error type from Result<T, E>.
fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
if_chain! {
if let ExprKind::Ret(Some(ref err_ret)) = arm.body.kind;
if let ExprKind::Call(ref from_error_path, ref from_error_args) = err_ret.kind;
if let ExprKind::Path(ref from_error_fn) = from_error_path.kind;
if match_qpath(from_error_fn, &paths::TRY_FROM_ERROR);
if let Some(from_error_arg) = from_error_args.get(0);
if let ty::Adt(_, subst) = ty.kind;
if is_type_diagnostic_item(cx, ty, sym!(result_type));
let err_ty = subst.type_at(1);
then {
Some(cx.typeck_results().expr_ty(from_error_arg))
Some(err_ty)
} else {
None
}
}
}
/// Extracts the error type from Poll<Result<T, E>>.
fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
if_chain! {
if let ty::Adt(def, subst) = ty.kind;
if match_def_path(cx, def.did, &paths::POLL);
let ready_ty = subst.type_at(0);
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind;
if cx.tcx.is_diagnostic_item(sym!(result_type), ready_def.did);
let err_ty = ready_subst.type_at(1);
then {
Some(err_ty)
} else {
None
}
}
}
/// Extracts the error type from Poll<Option<Result<T, E>>>.
fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
if_chain! {
if let ty::Adt(def, subst) = ty.kind;
if match_def_path(cx, def.did, &paths::POLL);
let ready_ty = subst.type_at(0);
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind;
if cx.tcx.is_diagnostic_item(sym!(option_type), ready_def.did);
let some_ty = ready_subst.type_at(0);
if let ty::Adt(some_def, some_subst) = some_ty.kind;
if cx.tcx.is_diagnostic_item(sym!(result_type), some_def.did);
let err_ty = some_subst.type_at(1);
then {
Some(err_ty)
} else {
None
}

View file

@ -181,8 +181,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
self.cx,
UNNECESSARY_UNWRAP,
expr.span,
&format!("You checked before that `{}()` cannot fail. \
Instead of checking and unwrapping, it's better to use `if let` or `match`.",
&format!("you checked before that `{}()` cannot fail, \
instead of checking and unwrapping, it's better to use `if let` or `match`",
method_name.ident.name),
|diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); },
);
@ -191,7 +191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
self.cx,
PANICKING_UNWRAP,
expr.span,
&format!("This call to `{}()` will always panic.",
&format!("this call to `{}()` will always panic",
method_name.ident.name),
|diag| { diag.span_label(unwrappable.check.span, "because of this check"); },
);

0
clippy_lints/src/utils/ast_utils.rs Executable file → Normal file
View file

View file

@ -75,12 +75,12 @@ pub fn get_attr<'a>(
})
.map_or_else(
|| {
sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute");
false
},
|deprecation_status| {
let mut diag =
sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute");
sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute");
match *deprecation_status {
DeprecationStatus::Deprecated => {
diag.emit();

View file

@ -43,6 +43,7 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable};
use rustc_mir::const_eval;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp;
use rustc_span::symbol::{self, kw, Symbol};
@ -52,7 +53,6 @@ use rustc_trait_selection::traits::query::normalize::AtExt;
use smallvec::SmallVec;
use crate::consts::{constant, Constant};
use crate::reexport::Name;
/// Returns `true` if the two spans come from differing expansions (i.e., one is
/// from a macro and one isn't).
@ -150,7 +150,7 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str])
}
/// Checks if an expression references a variable of the given name.
pub fn match_var(expr: &Expr<'_>, var: Name) -> bool {
pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
if let [p] = path.segments {
return p.ident.name == var;
@ -420,7 +420,7 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
}
/// Gets the name of the item the expression is in, if available.
pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Name> {
pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
match cx.tcx.hir().find(parent_id) {
Some(
@ -433,7 +433,7 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Name> {
}
/// Gets the name of a `Pat`, if any.
pub fn get_pat_name(pat: &Pat<'_>) -> Option<Name> {
pub fn get_pat_name(pat: &Pat<'_>) -> Option<Symbol> {
match pat.kind {
PatKind::Binding(.., ref spname, _) => Some(spname.name),
PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
@ -443,14 +443,14 @@ pub fn get_pat_name(pat: &Pat<'_>) -> Option<Name> {
}
struct ContainsName {
name: Name,
name: Symbol,
result: bool,
}
impl<'tcx> Visitor<'tcx> for ContainsName {
type Map = Map<'tcx>;
fn visit_name(&mut self, _: Span, name: Name) {
fn visit_name(&mut self, _: Span, name: Symbol) {
if self.name == name {
self.result = true;
}
@ -461,7 +461,7 @@ impl<'tcx> Visitor<'tcx> for ContainsName {
}
/// Checks if an `Expr` contains a certain name.
pub fn contains_name(name: Name, expr: &Expr<'_>) -> bool {
pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
let mut cn = ContainsName { name, result: false };
cn.visit_expr(expr);
cn.result
@ -869,11 +869,19 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
/// Checks if an expression is constructing a tuple-like enum variant or struct
pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool {
cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty()
}
if let ExprKind::Call(ref fun, _) = expr.kind {
if let ExprKind::Path(ref qp) = fun.kind {
let res = cx.qpath_res(qp, fun.hir_id);
return match res {
def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
// FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210
def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => {
const_eval::is_const_fn(cx.tcx, def_id)
},
def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
_ => false,
};
@ -1027,7 +1035,7 @@ pub fn is_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool
cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
}
pub fn get_arg_name(pat: &Pat<'_>) -> Option<Name> {
pub fn get_arg_name(pat: &Pat<'_>) -> Option<Symbol> {
match pat.kind {
PatKind::Binding(.., ident, None) => Some(ident.name),
PatKind::Ref(ref subpat, _) => get_arg_name(subpat),
@ -1378,6 +1386,36 @@ pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bo
})
}
/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point
/// number type, a str, or an array, slice, or tuple of those types).
pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
match ty.kind {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
ty::Ref(_, inner, _) if inner.kind == ty::Str => true,
ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
_ => false,
}
}
/// Returns true iff the given expression is a slice of primitives (as defined in the
/// `is_recursively_primitive_type` function).
pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
match expr_type.kind {
ty::Slice(ref element_type)
| ty::Ref(
_,
ty::TyS {
kind: ty::Slice(ref element_type),
..
},
_,
) => is_recursively_primitive_type(element_type),
_ => false,
}
}
#[macro_export]
macro_rules! unwrap_cargo_metadata {
($cx: ident, $lint: ident, $deps: expr) => {{

View file

@ -80,6 +80,7 @@ pub const PATH: [&str; 3] = ["std", "path", "Path"];
pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
@ -129,7 +130,6 @@ pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"];
pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"];
pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];

View file

@ -27,10 +27,7 @@ because that's clearly a non-descriptive name.
## Setup
When working on Clippy, you will need the current git master version of rustc,
which can change rapidly. Make sure you're working near rust-clippy's master,
and use the `setup-toolchain.sh` script to configure the appropriate toolchain
for the Clippy directory.
See the [Basics](basics.md#get-the-code) documentation.
## Getting Started
@ -38,12 +35,14 @@ There is a bit of boilerplate code that needs to be set up when creating a new
lint. Fortunately, you can use the clippy dev tools to handle this for you. We
are naming our new lint `foo_functions` (lints are generally written in snake
case), and we don't need type information so it will have an early pass type
(more on this later on). To get started on this lint you can run
`cargo dev new_lint --name=foo_functions --pass=early --category=pedantic`
(category will default to nursery if not provided). This command will create
two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`,
as well as run `cargo dev update_lints` to register the new lint. For cargo lints,
two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`.
(more on this later on). If you're not sure if the name you chose fits the lint,
take a look at our [lint naming guidelines][lint_naming]. To get started on this
lint you can run `cargo dev new_lint --name=foo_functions --pass=early
--category=pedantic` (category will default to nursery if not provided). This
command will create two files: `tests/ui/foo_functions.rs` and
`clippy_lints/src/foo_functions.rs`, as well as run `cargo dev update_lints` to
register the new lint. For cargo lints, two project hierarchies (fail/pass) will
be created by default under `tests/ui-cargo`.
Next, we'll open up these files and add our lint!

112
doc/basics.md Normal file
View file

@ -0,0 +1,112 @@
# Basics for hacking on Clippy
This document explains the basics for hacking on Clippy. Besides others, this
includes how to set-up the development environment, how to build and how to test
Clippy. For a more in depth description on the codebase take a look at [Adding
Lints] or [Common Tools].
[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md
- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy)
- [Get the code](#get-the-code)
- [Setup](#setup)
- [Building and Testing](#building-and-testing)
- [`cargo dev`](#cargo-dev)
## Get the Code
First, make sure you have checked out the latest version of Clippy. If this is
your first time working on Clippy, create a fork of the repository and clone it
afterwards with the following command:
```bash
git clone git@github.com:<your-username>/rust-clippy
```
If you've already cloned Clippy in the past, update it to the latest version:
```bash
# upstream has to be the remote of the rust-lang/rust-clippy repo
git fetch upstream
# make sure that you are on the master branch
git checkout master
# rebase your master branch on the upstream master
git rebase upstream/master
# push to the master branch of your fork
git push
```
## Setup
Next we need to setup the toolchain to compile Clippy. Since Clippy heavily
relies on compiler internals it is build with the latest rustc master. To get
this toolchain, you can just use the `setup-toolchain.sh` script or use
`rustup-toolchain-install-master`:
```bash
sh setup-toolchain.sh
# OR
cargo install rustup-toolchain-install-master
# For better IDE integration also add `-c rustfmt -c rust-src` (optional)
rustup-toolchain-install-master -f -n master -c rustc-dev -c llvm-tools
rustup override set master
```
_Note:_ Sometimes you may get compiler errors when building Clippy, even if you
didn't change anything. Normally those will be fixed by a maintainer in a few hours.
## Building and Testing
Once the `master` toolchain is installed, you can build and test Clippy like
every other Rust project:
```bash
cargo build # builds Clippy
cargo test # tests Clippy
```
Since Clippy's test suite is pretty big, there are some commands that only run a
subset of Clippy's tests:
```bash
# only run UI tests
cargo uitest
# only run UI tests starting with `test_`
TESTNAME="test_" cargo uitest
# only run dogfood tests
cargo test --test dogfood
```
If the output of a [UI test] differs from the expected output, you can update the
reference file with:
```bash
sh tests/ui/update-all-references.sh
```
For example, this is necessary, if you fix a typo in an error message of a lint
or if you modify a test file to add a test case.
_Note:_ This command may update more files than you intended. In that case only
commit the files you wanted to update.
[UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests
## `cargo dev`
Clippy has some dev tools to make working on Clippy more convenient. These tools
can be accessed through the `cargo dev` command. Available tools are listed
below. To get more information about these commands, just call them with
`--help`.
```bash
# formats the whole Clippy codebase and all tests
cargo dev fmt
# register or update lint names/groups/...
cargo dev update_lints
# create a new lint and register it
cargo dev new_lint
# (experimental) Setup Clippy to work with rust-analyzer
cargo dev ra-setup
```

View file

@ -360,6 +360,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "derive",
},
Lint {
name: "derive_ord_xor_partial_ord",
group: "correctness",
desc: "deriving `Ord` but implementing `PartialOrd` explicitly",
deprecation: None,
module: "derive",
},
Lint {
name: "diverging_sub_expression",
group: "complexity",
@ -405,7 +412,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
Lint {
name: "drop_bounds",
group: "correctness",
desc: "Bounds of the form `T: Drop` are useless",
desc: "bounds of the form `T: Drop` are useless",
deprecation: None,
module: "drop_bounds",
},
@ -1452,6 +1459,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "bytecount",
},
Lint {
name: "needless_arbitrary_self_type",
group: "complexity",
desc: "type of `self` parameter is already by default `Self`",
deprecation: None,
module: "needless_arbitrary_self_type",
},
Lint {
name: "needless_bool",
group: "complexity",
@ -1928,6 +1942,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "copies",
},
Lint {
name: "same_item_push",
group: "style",
desc: "the same item is pushed inside of a for loop",
deprecation: None,
module: "loops",
},
Lint {
name: "search_is_some",
group: "complexity",
@ -2026,6 +2047,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "slow_vector_initialization",
},
Lint {
name: "stable_sort_primitive",
group: "perf",
desc: "use of sort() when sort_unstable() is equivalent",
deprecation: None,
module: "stable_sort_primitive",
},
Lint {
name: "string_add",
group: "restriction",
@ -2166,6 +2194,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "misc",
},
Lint {
name: "trait_duplication_in_bounds",
group: "pedantic",
desc: "Check if the same trait bounds are specified twice during a function declaration",
deprecation: None,
module: "trait_bounds",
},
Lint {
name: "transmute_bytes_to_str",
group: "complexity",

View file

@ -3,7 +3,7 @@
use compiletest_rs as compiletest;
use compiletest_rs::common::Mode as TestMode;
use std::env::{self, set_var};
use std::env::{self, set_var, var};
use std::ffi::OsStr;
use std::fs;
use std::io;
@ -136,7 +136,9 @@ fn run_ui_toml(config: &mut compiletest::Config) {
let tests = compiletest::make_tests(&config);
let manifest_dir = var("CARGO_MANIFEST_DIR").unwrap_or_default();
let res = run_tests(&config, tests);
set_var("CARGO_MANIFEST_DIR", &manifest_dir);
match res {
Ok(true) => {},
Ok(false) => panic!("Some tests failed"),
@ -147,9 +149,6 @@ fn run_ui_toml(config: &mut compiletest::Config) {
}
fn run_ui_cargo(config: &mut compiletest::Config) {
if cargo::is_rustc_test_suite() {
return;
}
fn run_tests(
config: &compiletest::Config,
filter: &Option<String>,
@ -217,6 +216,10 @@ fn run_ui_cargo(config: &mut compiletest::Config) {
Ok(result)
}
if cargo::is_rustc_test_suite() {
return;
}
config.mode = TestMode::Ui;
config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();

View file

@ -1,4 +1,4 @@
error: This function has a large number of lines.
error: this function has a large number of lines
--> $DIR/test.rs:18:1
|
LL | / fn too_many_lines() {
@ -9,7 +9,7 @@ LL | | }
|
= note: `-D clippy::too-many-lines` implied by `-D warnings`
error: This function has a large number of lines.
error: this function has a large number of lines
--> $DIR/test.rs:38:1
|
LL | / fn comment_before_code() {

View file

@ -47,6 +47,7 @@ async fn not_good(x: &Mutex<u32>) -> u32 {
first + second + third
}
#[allow(clippy::manual_async_fn)]
fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
async move {
let guard = x.lock().unwrap();

View file

@ -46,13 +46,13 @@ LL | | };
| |_____^
error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.
--> $DIR/await_holding_lock.rs:52:13
--> $DIR/await_holding_lock.rs:53:13
|
LL | let guard = x.lock().unwrap();
| ^^^^^
|
note: these are all the await points this lock is held through
--> $DIR/await_holding_lock.rs:52:9
--> $DIR/await_holding_lock.rs:53:9
|
LL | / let guard = x.lock().unwrap();
LL | | baz().await

View file

@ -84,25 +84,25 @@ error: order comparisons between booleans can be simplified
LL | if x > y {
| ^^^^^ help: try simplifying it as shown: `x & !y`
error: This comparison might be written more concisely
error: this comparison might be written more concisely
--> $DIR/bool_comparison.rs:120:8
|
LL | if a == !b {};
| ^^^^^^^ help: try simplifying it as shown: `a != b`
error: This comparison might be written more concisely
error: this comparison might be written more concisely
--> $DIR/bool_comparison.rs:121:8
|
LL | if !a == b {};
| ^^^^^^^ help: try simplifying it as shown: `a != b`
error: This comparison might be written more concisely
error: this comparison might be written more concisely
--> $DIR/bool_comparison.rs:125:8
|
LL | if b == !a {};
| ^^^^^^^ help: try simplifying it as shown: `b != a`
error: This comparison might be written more concisely
error: this comparison might be written more concisely
--> $DIR/bool_comparison.rs:126:8
|
LL | if !b == a {};

View file

@ -1,4 +1,4 @@
error: This generic shadows the built-in type `u32`
error: this generic shadows the built-in type `u32`
--> $DIR/builtin-type-shadow.rs:4:8
|
LL | fn foo<u32>(a: u32) -> u32 {

View file

@ -1,8 +1,8 @@
error: You appear to be counting bytes the naive way
error: you appear to be counting bytes the naive way
--> $DIR/bytecount.rs:5:13
|
LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, 0)`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, 0)`
|
note: the lint level is defined here
--> $DIR/bytecount.rs:1:8
@ -10,17 +10,17 @@ note: the lint level is defined here
LL | #[deny(clippy::naive_bytecount)]
| ^^^^^^^^^^^^^^^^^^^^^^^
error: You appear to be counting bytes the naive way
error: you appear to be counting bytes the naive way
--> $DIR/bytecount.rs:7:13
|
LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count((&x[..]), 0)`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count((&x[..]), 0)`
error: You appear to be counting bytes the naive way
error: you appear to be counting bytes the naive way
--> $DIR/bytecount.rs:19:13
|
LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, b + 1)`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, b + 1)`
error: aborting due to 3 previous errors

View file

@ -1,4 +1,4 @@
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:17:13
|
LL | let _ = value <= (u32::max_value() as i64) && value >= 0;
@ -6,91 +6,91 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0;
|
= note: `-D clippy::checked-conversions` implied by `-D warnings`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:18:13
|
LL | let _ = value <= (u32::MAX as i64) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:22:13
|
LL | let _ = value <= i64::from(u16::max_value()) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:23:13
|
LL | let _ = value <= i64::from(u16::MAX) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:27:13
|
LL | let _ = value <= (u8::max_value() as isize) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:28:13
|
LL | let _ = value <= (u8::MAX as isize) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:34:13
|
LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:35:13
|
LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:39:13
|
LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:40:13
|
LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:46:13
|
LL | let _ = value <= i32::max_value() as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:47:13
|
LL | let _ = value <= i32::MAX as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:51:13
|
LL | let _ = value <= isize::max_value() as usize && value as i32 == 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:52:13
|
LL | let _ = value <= isize::MAX as usize && value as i32 == 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:56:13
|
LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
error: Checked cast can be simplified.
error: checked cast can be simplified
--> $DIR/checked_conversions.rs:57:13
|
LL | let _ = value <= u16::MAX as u32 && value as i32 == 5;

View file

@ -1,4 +1,4 @@
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/complex_conditionals.rs:8:9
|
LL | if x.is_ok() && y.is_err() {
@ -12,7 +12,7 @@ note: the lint level is defined here
LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: This call to `unwrap_err()` will always panic.
error: this call to `unwrap_err()` will always panic
--> $DIR/complex_conditionals.rs:9:9
|
LL | if x.is_ok() && y.is_err() {
@ -27,7 +27,7 @@ note: the lint level is defined here
LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: This call to `unwrap()` will always panic.
error: this call to `unwrap()` will always panic
--> $DIR/complex_conditionals.rs:10:9
|
LL | if x.is_ok() && y.is_err() {
@ -36,7 +36,7 @@ LL | if x.is_ok() && y.is_err() {
LL | y.unwrap(); // will panic
| ^^^^^^^^^^
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/complex_conditionals.rs:11:9
|
LL | if x.is_ok() && y.is_err() {
@ -45,7 +45,7 @@ LL | if x.is_ok() && y.is_err() {
LL | y.unwrap_err(); // unnecessary
| ^^^^^^^^^^^^^^
error: This call to `unwrap()` will always panic.
error: this call to `unwrap()` will always panic
--> $DIR/complex_conditionals.rs:25:9
|
LL | if x.is_ok() || y.is_ok() {
@ -54,7 +54,7 @@ LL | if x.is_ok() || y.is_ok() {
LL | x.unwrap(); // will panic
| ^^^^^^^^^^
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/complex_conditionals.rs:26:9
|
LL | if x.is_ok() || y.is_ok() {
@ -63,7 +63,7 @@ LL | if x.is_ok() || y.is_ok() {
LL | x.unwrap_err(); // unnecessary
| ^^^^^^^^^^^^^^
error: This call to `unwrap()` will always panic.
error: this call to `unwrap()` will always panic
--> $DIR/complex_conditionals.rs:27:9
|
LL | if x.is_ok() || y.is_ok() {
@ -72,7 +72,7 @@ LL | if x.is_ok() || y.is_ok() {
LL | y.unwrap(); // will panic
| ^^^^^^^^^^
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/complex_conditionals.rs:28:9
|
LL | if x.is_ok() || y.is_ok() {
@ -81,7 +81,7 @@ LL | if x.is_ok() || y.is_ok() {
LL | y.unwrap_err(); // unnecessary
| ^^^^^^^^^^^^^^
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/complex_conditionals.rs:32:9
|
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
@ -89,7 +89,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
LL | x.unwrap(); // unnecessary
| ^^^^^^^^^^
error: This call to `unwrap_err()` will always panic.
error: this call to `unwrap_err()` will always panic
--> $DIR/complex_conditionals.rs:33:9
|
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
@ -98,7 +98,7 @@ LL | x.unwrap(); // unnecessary
LL | x.unwrap_err(); // will panic
| ^^^^^^^^^^^^^^
error: This call to `unwrap()` will always panic.
error: this call to `unwrap()` will always panic
--> $DIR/complex_conditionals.rs:34:9
|
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
@ -107,7 +107,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
LL | y.unwrap(); // will panic
| ^^^^^^^^^^
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/complex_conditionals.rs:35:9
|
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
@ -116,7 +116,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
LL | y.unwrap_err(); // unnecessary
| ^^^^^^^^^^^^^^
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/complex_conditionals.rs:36:9
|
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
@ -125,7 +125,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
LL | z.unwrap(); // unnecessary
| ^^^^^^^^^^
error: This call to `unwrap_err()` will always panic.
error: this call to `unwrap_err()` will always panic
--> $DIR/complex_conditionals.rs:37:9
|
LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
@ -134,7 +134,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) {
LL | z.unwrap_err(); // will panic
| ^^^^^^^^^^^^^^
error: This call to `unwrap()` will always panic.
error: this call to `unwrap()` will always panic
--> $DIR/complex_conditionals.rs:45:9
|
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
@ -143,7 +143,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
LL | x.unwrap(); // will panic
| ^^^^^^^^^^
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/complex_conditionals.rs:46:9
|
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
@ -152,7 +152,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
LL | x.unwrap_err(); // unnecessary
| ^^^^^^^^^^^^^^
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/complex_conditionals.rs:47:9
|
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
@ -161,7 +161,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
LL | y.unwrap(); // unnecessary
| ^^^^^^^^^^
error: This call to `unwrap_err()` will always panic.
error: this call to `unwrap_err()` will always panic
--> $DIR/complex_conditionals.rs:48:9
|
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
@ -170,7 +170,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
LL | y.unwrap_err(); // will panic
| ^^^^^^^^^^^^^^
error: This call to `unwrap()` will always panic.
error: this call to `unwrap()` will always panic
--> $DIR/complex_conditionals.rs:49:9
|
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
@ -179,7 +179,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {
LL | z.unwrap(); // will panic
| ^^^^^^^^^^
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/complex_conditionals.rs:50:9
|
LL | if x.is_ok() || !(y.is_ok() && z.is_err()) {

View file

@ -1,4 +1,4 @@
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/complex_conditionals_nested.rs:8:13
|
LL | if x.is_some() {
@ -12,7 +12,7 @@ note: the lint level is defined here
LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: This call to `unwrap()` will always panic.
error: this call to `unwrap()` will always panic
--> $DIR/complex_conditionals_nested.rs:10:13
|
LL | if x.is_some() {

View file

@ -1,4 +1,4 @@
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/simple_conditionals.rs:39:9
|
LL | if x.is_some() {
@ -12,7 +12,7 @@ note: the lint level is defined here
LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: This call to `unwrap()` will always panic.
error: this call to `unwrap()` will always panic
--> $DIR/simple_conditionals.rs:41:9
|
LL | if x.is_some() {
@ -27,7 +27,7 @@ note: the lint level is defined here
LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: This call to `unwrap()` will always panic.
error: this call to `unwrap()` will always panic
--> $DIR/simple_conditionals.rs:44:9
|
LL | if x.is_none() {
@ -35,7 +35,7 @@ LL | if x.is_none() {
LL | x.unwrap(); // will panic
| ^^^^^^^^^^
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/simple_conditionals.rs:46:9
|
LL | if x.is_none() {
@ -44,7 +44,7 @@ LL | if x.is_none() {
LL | x.unwrap(); // unnecessary
| ^^^^^^^^^^
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/simple_conditionals.rs:7:13
|
LL | if $a.is_some() {
@ -57,7 +57,7 @@ LL | m!(x);
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/simple_conditionals.rs:54:9
|
LL | if x.is_ok() {
@ -65,7 +65,7 @@ LL | if x.is_ok() {
LL | x.unwrap(); // unnecessary
| ^^^^^^^^^^
error: This call to `unwrap_err()` will always panic.
error: this call to `unwrap_err()` will always panic
--> $DIR/simple_conditionals.rs:55:9
|
LL | if x.is_ok() {
@ -74,7 +74,7 @@ LL | x.unwrap(); // unnecessary
LL | x.unwrap_err(); // will panic
| ^^^^^^^^^^^^^^
error: This call to `unwrap()` will always panic.
error: this call to `unwrap()` will always panic
--> $DIR/simple_conditionals.rs:57:9
|
LL | if x.is_ok() {
@ -83,7 +83,7 @@ LL | if x.is_ok() {
LL | x.unwrap(); // will panic
| ^^^^^^^^^^
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/simple_conditionals.rs:58:9
|
LL | if x.is_ok() {
@ -92,7 +92,7 @@ LL | if x.is_ok() {
LL | x.unwrap_err(); // unnecessary
| ^^^^^^^^^^^^^^
error: This call to `unwrap()` will always panic.
error: this call to `unwrap()` will always panic
--> $DIR/simple_conditionals.rs:61:9
|
LL | if x.is_err() {
@ -100,7 +100,7 @@ LL | if x.is_err() {
LL | x.unwrap(); // will panic
| ^^^^^^^^^^
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/simple_conditionals.rs:62:9
|
LL | if x.is_err() {
@ -109,7 +109,7 @@ LL | x.unwrap(); // will panic
LL | x.unwrap_err(); // unnecessary
| ^^^^^^^^^^^^^^
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
--> $DIR/simple_conditionals.rs:64:9
|
LL | if x.is_err() {
@ -118,7 +118,7 @@ LL | if x.is_err() {
LL | x.unwrap(); // unnecessary
| ^^^^^^^^^^
error: This call to `unwrap_err()` will always panic.
error: this call to `unwrap_err()` will always panic
--> $DIR/simple_conditionals.rs:65:9
|
LL | if x.is_err() {

View file

@ -1,4 +1,4 @@
error: Comparing with null is better expressed by the `.is_null()` method
error: comparing with null is better expressed by the `.is_null()` method
--> $DIR/cmp_null.rs:9:8
|
LL | if p == ptr::null() {
@ -6,7 +6,7 @@ LL | if p == ptr::null() {
|
= note: `-D clippy::cmp-null` implied by `-D warnings`
error: Comparing with null is better expressed by the `.is_null()` method
error: comparing with null is better expressed by the `.is_null()` method
--> $DIR/cmp_null.rs:14:8
|
LL | if m == ptr::null_mut() {

View file

@ -0,0 +1,5 @@
#![warn(clippy::needless_collect)]
fn main() {
let _ = vec![1, 2, 3].into_iter().collect::<Vec<_>>().is_empty();
}

View file

@ -0,0 +1,10 @@
error: avoid using `collect()` when not needed
--> $DIR/ice-5872.rs:4:39
|
LL | let _ = vec![1, 2, 3].into_iter().collect::<Vec<_>>().is_empty();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
|
= note: `-D clippy::needless-collect` implied by `-D warnings`
error: aborting due to previous error

View file

@ -1,4 +1,4 @@
error: Calling `std::string::String::default()` is more clear than this expression
error: calling `std::string::String::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:8:22
|
LL | let s1: String = Default::default();
@ -6,43 +6,43 @@ LL | let s1: String = Default::default();
|
= note: `-D clippy::default-trait-access` implied by `-D warnings`
error: Calling `std::string::String::default()` is more clear than this expression
error: calling `std::string::String::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:12:22
|
LL | let s3: String = D2::default();
| ^^^^^^^^^^^^^ help: try: `std::string::String::default()`
error: Calling `std::string::String::default()` is more clear than this expression
error: calling `std::string::String::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:14:22
|
LL | let s4: String = std::default::Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
error: Calling `std::string::String::default()` is more clear than this expression
error: calling `std::string::String::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:18:22
|
LL | let s6: String = default::Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
error: Calling `GenericDerivedDefault<std::string::String>::default()` is more clear than this expression
error: calling `GenericDerivedDefault<std::string::String>::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:28:46
|
LL | let s11: GenericDerivedDefault<String> = Default::default();
| ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault<std::string::String>::default()`
error: Calling `TupleDerivedDefault::default()` is more clear than this expression
error: calling `TupleDerivedDefault::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:34:36
|
LL | let s14: TupleDerivedDefault = Default::default();
| ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()`
error: Calling `ArrayDerivedDefault::default()` is more clear than this expression
error: calling `ArrayDerivedDefault::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:36:36
|
LL | let s15: ArrayDerivedDefault = Default::default();
| ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()`
error: Calling `TupleStructDerivedDefault::default()` is more clear than this expression
error: calling `TupleStructDerivedDefault::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:40:42
|
LL | let s17: TupleStructDerivedDefault = Default::default();

View file

@ -0,0 +1,68 @@
#![warn(clippy::derive_ord_xor_partial_ord)]
use std::cmp::Ordering;
#[derive(PartialOrd, Ord, PartialEq, Eq)]
struct DeriveBoth;
impl PartialEq<u64> for DeriveBoth {
fn eq(&self, _: &u64) -> bool {
true
}
}
impl PartialOrd<u64> for DeriveBoth {
fn partial_cmp(&self, _: &u64) -> Option<Ordering> {
Some(Ordering::Equal)
}
}
#[derive(Ord, PartialEq, Eq)]
struct DeriveOrd;
impl PartialOrd for DeriveOrd {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(other.cmp(self))
}
}
#[derive(Ord, PartialEq, Eq)]
struct DeriveOrdWithExplicitTypeVariable;
impl PartialOrd<DeriveOrdWithExplicitTypeVariable> for DeriveOrdWithExplicitTypeVariable {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(other.cmp(self))
}
}
#[derive(PartialOrd, PartialEq, Eq)]
struct DerivePartialOrd;
impl std::cmp::Ord for DerivePartialOrd {
fn cmp(&self, other: &Self) -> Ordering {
Ordering::Less
}
}
#[derive(PartialOrd, PartialEq, Eq)]
struct ImplUserOrd;
trait Ord {}
// We don't want to lint on user-defined traits called `Ord`
impl Ord for ImplUserOrd {}
mod use_ord {
use std::cmp::{Ord, Ordering};
#[derive(PartialOrd, PartialEq, Eq)]
struct DerivePartialOrdInUseOrd;
impl Ord for DerivePartialOrdInUseOrd {
fn cmp(&self, other: &Self) -> Ordering {
Ordering::Less
}
}
}
fn main() {}

View file

@ -0,0 +1,71 @@
error: you are deriving `Ord` but have implemented `PartialOrd` explicitly
--> $DIR/derive_ord_xor_partial_ord.rs:20:10
|
LL | #[derive(Ord, PartialEq, Eq)]
| ^^^
|
= note: `-D clippy::derive-ord-xor-partial-ord` implied by `-D warnings`
note: `PartialOrd` implemented here
--> $DIR/derive_ord_xor_partial_ord.rs:23:1
|
LL | / impl PartialOrd for DeriveOrd {
LL | | fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
LL | | Some(other.cmp(self))
LL | | }
LL | | }
| |_^
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: you are deriving `Ord` but have implemented `PartialOrd` explicitly
--> $DIR/derive_ord_xor_partial_ord.rs:29:10
|
LL | #[derive(Ord, PartialEq, Eq)]
| ^^^
|
note: `PartialOrd` implemented here
--> $DIR/derive_ord_xor_partial_ord.rs:32:1
|
LL | / impl PartialOrd<DeriveOrdWithExplicitTypeVariable> for DeriveOrdWithExplicitTypeVariable {
LL | | fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
LL | | Some(other.cmp(self))
LL | | }
LL | | }
| |_^
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: you are implementing `Ord` explicitly but have derived `PartialOrd`
--> $DIR/derive_ord_xor_partial_ord.rs:41:1
|
LL | / impl std::cmp::Ord for DerivePartialOrd {
LL | | fn cmp(&self, other: &Self) -> Ordering {
LL | | Ordering::Less
LL | | }
LL | | }
| |_^
|
note: `PartialOrd` implemented here
--> $DIR/derive_ord_xor_partial_ord.rs:38:10
|
LL | #[derive(PartialOrd, PartialEq, Eq)]
| ^^^^^^^^^^
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: you are implementing `Ord` explicitly but have derived `PartialOrd`
--> $DIR/derive_ord_xor_partial_ord.rs:61:5
|
LL | / impl Ord for DerivePartialOrdInUseOrd {
LL | | fn cmp(&self, other: &Self) -> Ordering {
LL | | Ordering::Less
LL | | }
LL | | }
| |_____^
|
note: `PartialOrd` implemented here
--> $DIR/derive_ord_xor_partial_ord.rs:58:14
|
LL | #[derive(PartialOrd, PartialEq, Eq)]
| ^^^^^^^^^^
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 4 previous errors

View file

@ -1,4 +1,4 @@
error: This binary expression can be simplified
error: this binary expression can be simplified
--> $DIR/double_comparison.rs:6:8
|
LL | if x == y || x < y {
@ -6,43 +6,43 @@ LL | if x == y || x < y {
|
= note: `-D clippy::double-comparisons` implied by `-D warnings`
error: This binary expression can be simplified
error: this binary expression can be simplified
--> $DIR/double_comparison.rs:9:8
|
LL | if x < y || x == y {
| ^^^^^^^^^^^^^^^ help: try: `x <= y`
error: This binary expression can be simplified
error: this binary expression can be simplified
--> $DIR/double_comparison.rs:12:8
|
LL | if x == y || x > y {
| ^^^^^^^^^^^^^^^ help: try: `x >= y`
error: This binary expression can be simplified
error: this binary expression can be simplified
--> $DIR/double_comparison.rs:15:8
|
LL | if x > y || x == y {
| ^^^^^^^^^^^^^^^ help: try: `x >= y`
error: This binary expression can be simplified
error: this binary expression can be simplified
--> $DIR/double_comparison.rs:18:8
|
LL | if x < y || x > y {
| ^^^^^^^^^^^^^^ help: try: `x != y`
error: This binary expression can be simplified
error: this binary expression can be simplified
--> $DIR/double_comparison.rs:21:8
|
LL | if x > y || x < y {
| ^^^^^^^^^^^^^^ help: try: `x != y`
error: This binary expression can be simplified
error: this binary expression can be simplified
--> $DIR/double_comparison.rs:24:8
|
LL | if x <= y && x >= y {
| ^^^^^^^^^^^^^^^^ help: try: `x == y`
error: This binary expression can be simplified
error: this binary expression can be simplified
--> $DIR/double_comparison.rs:27:8
|
LL | if x >= y && x <= y {

View file

@ -1,4 +1,4 @@
error: Consider removing unnecessary double parentheses
error: consider removing unnecessary double parentheses
--> $DIR/double_parens.rs:15:5
|
LL | ((0))
@ -6,31 +6,31 @@ LL | ((0))
|
= note: `-D clippy::double-parens` implied by `-D warnings`
error: Consider removing unnecessary double parentheses
error: consider removing unnecessary double parentheses
--> $DIR/double_parens.rs:19:14
|
LL | dummy_fn((0));
| ^^^
error: Consider removing unnecessary double parentheses
error: consider removing unnecessary double parentheses
--> $DIR/double_parens.rs:23:20
|
LL | x.dummy_method((0));
| ^^^
error: Consider removing unnecessary double parentheses
error: consider removing unnecessary double parentheses
--> $DIR/double_parens.rs:27:5
|
LL | ((1, 2))
| ^^^^^^^^
error: Consider removing unnecessary double parentheses
error: consider removing unnecessary double parentheses
--> $DIR/double_parens.rs:31:5
|
LL | (())
| ^^^^
error: Consider removing unnecessary double parentheses
error: consider removing unnecessary double parentheses
--> $DIR/double_parens.rs:53:16
|
LL | assert_eq!(((1, 2)), (1, 2), "Error");

View file

@ -1,4 +1,4 @@
error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue.
error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue
--> $DIR/drop_bounds.rs:2:11
|
LL | fn foo<T: Drop>() {}
@ -6,7 +6,7 @@ LL | fn foo<T: Drop>() {}
|
= note: `#[deny(clippy::drop_bounds)]` on by default
error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue.
error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue
--> $DIR/drop_bounds.rs:5:8
|
LL | T: Drop,

View file

@ -1,4 +1,4 @@
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
--> $DIR/empty_line_after_outer_attribute.rs:11:1
|
LL | / #[crate_type = "lib"]
@ -9,7 +9,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) }
|
= note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
--> $DIR/empty_line_after_outer_attribute.rs:23:1
|
LL | / #[crate_type = "lib"]
@ -17,7 +17,7 @@ LL | |
LL | | fn with_one_newline() { assert!(true) }
| |_
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
--> $DIR/empty_line_after_outer_attribute.rs:28:1
|
LL | / #[crate_type = "lib"]
@ -26,7 +26,7 @@ LL | |
LL | | fn with_two_newlines() { assert!(true) }
| |_
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
--> $DIR/empty_line_after_outer_attribute.rs:35:1
|
LL | / #[crate_type = "lib"]
@ -34,7 +34,7 @@ LL | |
LL | | enum Baz {
| |_
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
--> $DIR/empty_line_after_outer_attribute.rs:43:1
|
LL | / #[crate_type = "lib"]
@ -42,7 +42,7 @@ LL | |
LL | | struct Foo {
| |_
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
--> $DIR/empty_line_after_outer_attribute.rs:51:1
|
LL | / #[crate_type = "lib"]

View file

@ -1,4 +1,10 @@
#![allow(unused, dead_code, clippy::needless_lifetimes, clippy::needless_pass_by_value)]
#![allow(
unused,
dead_code,
clippy::needless_lifetimes,
clippy::needless_pass_by_value,
clippy::needless_arbitrary_self_type
)]
#![warn(clippy::extra_unused_lifetimes)]
fn empty() {}

View file

@ -1,5 +1,5 @@
error: this lifetime isn't used in the function definition
--> $DIR/extra_unused_lifetimes.rs:8:14
--> $DIR/extra_unused_lifetimes.rs:14:14
|
LL | fn unused_lt<'a>(x: u8) {}
| ^^
@ -7,19 +7,19 @@ LL | fn unused_lt<'a>(x: u8) {}
= note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings`
error: this lifetime isn't used in the function definition
--> $DIR/extra_unused_lifetimes.rs:10:25
--> $DIR/extra_unused_lifetimes.rs:16:25
|
LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) {
| ^^
error: this lifetime isn't used in the function definition
--> $DIR/extra_unused_lifetimes.rs:35:10
--> $DIR/extra_unused_lifetimes.rs:41:10
|
LL | fn x<'a>(&self) {}
| ^^
error: this lifetime isn't used in the function definition
--> $DIR/extra_unused_lifetimes.rs:61:22
--> $DIR/extra_unused_lifetimes.rs:67:22
|
LL | fn unused_lt<'a>(x: u8) {}
| ^^

View file

@ -1,4 +1,4 @@
error: This function has a large number of lines.
error: this function has a large number of lines
--> $DIR/functions_maxlines.rs:58:1
|
LL | / fn bad_lines() {

View file

@ -0,0 +1,22 @@
// run-rustfix
// aux-build:option_helpers.rs
#![warn(clippy::iter_skip_next)]
#![allow(clippy::blacklisted_name)]
#![allow(clippy::iter_nth)]
extern crate option_helpers;
use option_helpers::IteratorFalsePositives;
/// Checks implementation of `ITER_SKIP_NEXT` lint
fn main() {
let some_vec = vec![0, 1, 2, 3];
let _ = some_vec.iter().nth(42);
let _ = some_vec.iter().cycle().nth(42);
let _ = (1..10).nth(10);
let _ = &some_vec[..].iter().nth(3);
let foo = IteratorFalsePositives { foo: 0 };
let _ = foo.skip(42).next();
let _ = foo.filter().skip(42).next();
}

View file

@ -1,15 +1,17 @@
// run-rustfix
// aux-build:option_helpers.rs
#![warn(clippy::iter_skip_next)]
#![allow(clippy::blacklisted_name)]
#![allow(clippy::iter_nth)]
extern crate option_helpers;
use option_helpers::IteratorFalsePositives;
/// Checks implementation of `ITER_SKIP_NEXT` lint
fn iter_skip_next() {
let mut some_vec = vec![0, 1, 2, 3];
fn main() {
let some_vec = vec![0, 1, 2, 3];
let _ = some_vec.iter().skip(42).next();
let _ = some_vec.iter().cycle().skip(42).next();
let _ = (1..10).skip(10).next();
@ -18,5 +20,3 @@ fn iter_skip_next() {
let _ = foo.skip(42).next();
let _ = foo.filter().skip(42).next();
}
fn main() {}

View file

@ -1,35 +1,28 @@
error: called `skip(x).next()` on an iterator
--> $DIR/iter_skip_next.rs:13:13
--> $DIR/iter_skip_next.rs:15:28
|
LL | let _ = some_vec.iter().skip(42).next();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
|
= note: `-D clippy::iter-skip-next` implied by `-D warnings`
= help: this is more succinctly expressed by calling `nth(x)`
error: called `skip(x).next()` on an iterator
--> $DIR/iter_skip_next.rs:14:13
--> $DIR/iter_skip_next.rs:16:36
|
LL | let _ = some_vec.iter().cycle().skip(42).next();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: this is more succinctly expressed by calling `nth(x)`
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
error: called `skip(x).next()` on an iterator
--> $DIR/iter_skip_next.rs:15:13
--> $DIR/iter_skip_next.rs:17:20
|
LL | let _ = (1..10).skip(10).next();
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: this is more succinctly expressed by calling `nth(x)`
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)`
error: called `skip(x).next()` on an iterator
--> $DIR/iter_skip_next.rs:16:14
--> $DIR/iter_skip_next.rs:18:33
|
LL | let _ = &some_vec[..].iter().skip(3).next();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: this is more succinctly expressed by calling `nth(x)`
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)`
error: aborting due to 4 previous errors

View file

@ -4,14 +4,14 @@
pub struct PubOne;
impl PubOne {
pub fn len(self: &Self) -> isize {
pub fn len(&self) -> isize {
1
}
}
impl PubOne {
// A second impl for this struct -- the error span shouldn't mention this.
pub fn irrelevant(self: &Self) -> bool {
pub fn irrelevant(&self) -> bool {
false
}
}
@ -21,7 +21,7 @@ pub struct PubAllowed;
#[allow(clippy::len_without_is_empty)]
impl PubAllowed {
pub fn len(self: &Self) -> isize {
pub fn len(&self) -> isize {
1
}
}
@ -29,17 +29,17 @@ impl PubAllowed {
// No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the
// impl containing `len`.
impl PubAllowed {
pub fn irrelevant(self: &Self) -> bool {
pub fn irrelevant(&self) -> bool {
false
}
}
pub trait PubTraitsToo {
fn len(self: &Self) -> isize;
fn len(&self) -> isize;
}
impl PubTraitsToo for One {
fn len(self: &Self) -> isize {
fn len(&self) -> isize {
0
}
}
@ -47,11 +47,11 @@ impl PubTraitsToo for One {
pub struct HasIsEmpty;
impl HasIsEmpty {
pub fn len(self: &Self) -> isize {
pub fn len(&self) -> isize {
1
}
fn is_empty(self: &Self) -> bool {
fn is_empty(&self) -> bool {
false
}
}
@ -59,11 +59,11 @@ impl HasIsEmpty {
pub struct HasWrongIsEmpty;
impl HasWrongIsEmpty {
pub fn len(self: &Self) -> isize {
pub fn len(&self) -> isize {
1
}
pub fn is_empty(self: &Self, x: u32) -> bool {
pub fn is_empty(&self, x: u32) -> bool {
false
}
}
@ -71,7 +71,7 @@ impl HasWrongIsEmpty {
struct NotPubOne;
impl NotPubOne {
pub fn len(self: &Self) -> isize {
pub fn len(&self) -> isize {
// No error; `len` is pub but `NotPubOne` is not exported anyway.
1
}
@ -80,19 +80,19 @@ impl NotPubOne {
struct One;
impl One {
fn len(self: &Self) -> isize {
fn len(&self) -> isize {
// No error; `len` is private; see issue #1085.
1
}
}
trait TraitsToo {
fn len(self: &Self) -> isize;
fn len(&self) -> isize;
// No error; `len` is private; see issue #1085.
}
impl TraitsToo for One {
fn len(self: &Self) -> isize {
fn len(&self) -> isize {
0
}
}
@ -100,11 +100,11 @@ impl TraitsToo for One {
struct HasPrivateIsEmpty;
impl HasPrivateIsEmpty {
pub fn len(self: &Self) -> isize {
pub fn len(&self) -> isize {
1
}
fn is_empty(self: &Self) -> bool {
fn is_empty(&self) -> bool {
false
}
}
@ -112,16 +112,16 @@ impl HasPrivateIsEmpty {
struct Wither;
pub trait WithIsEmpty {
fn len(self: &Self) -> isize;
fn is_empty(self: &Self) -> bool;
fn len(&self) -> isize;
fn is_empty(&self) -> bool;
}
impl WithIsEmpty for Wither {
fn len(self: &Self) -> isize {
fn len(&self) -> isize {
1
}
fn is_empty(self: &Self) -> bool {
fn is_empty(&self) -> bool {
false
}
}

View file

@ -2,7 +2,7 @@ error: item `PubOne` has a public `len` method but no corresponding `is_empty` m
--> $DIR/len_without_is_empty.rs:6:1
|
LL | / impl PubOne {
LL | | pub fn len(self: &Self) -> isize {
LL | | pub fn len(&self) -> isize {
LL | | 1
LL | | }
LL | | }
@ -14,7 +14,7 @@ error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_e
--> $DIR/len_without_is_empty.rs:37:1
|
LL | / pub trait PubTraitsToo {
LL | | fn len(self: &Self) -> isize;
LL | | fn len(&self) -> isize;
LL | | }
| |_^
@ -22,7 +22,7 @@ error: item `HasIsEmpty` has a public `len` method but a private `is_empty` meth
--> $DIR/len_without_is_empty.rs:49:1
|
LL | / impl HasIsEmpty {
LL | | pub fn len(self: &Self) -> isize {
LL | | pub fn len(&self) -> isize {
LL | | 1
LL | | }
... |
@ -34,7 +34,7 @@ error: item `HasWrongIsEmpty` has a public `len` method but no corresponding `is
--> $DIR/len_without_is_empty.rs:61:1
|
LL | / impl HasWrongIsEmpty {
LL | | pub fn len(self: &Self) -> isize {
LL | | pub fn len(&self) -> isize {
LL | | 1
LL | | }
... |

View file

@ -7,12 +7,12 @@ pub struct One;
struct Wither;
trait TraitsToo {
fn len(self: &Self) -> isize;
fn len(&self) -> isize;
// No error; `len` is private; see issue #1085.
}
impl TraitsToo for One {
fn len(self: &Self) -> isize {
fn len(&self) -> isize {
0
}
}
@ -20,11 +20,11 @@ impl TraitsToo for One {
pub struct HasIsEmpty;
impl HasIsEmpty {
pub fn len(self: &Self) -> isize {
pub fn len(&self) -> isize {
1
}
fn is_empty(self: &Self) -> bool {
fn is_empty(&self) -> bool {
false
}
}
@ -32,26 +32,26 @@ impl HasIsEmpty {
pub struct HasWrongIsEmpty;
impl HasWrongIsEmpty {
pub fn len(self: &Self) -> isize {
pub fn len(&self) -> isize {
1
}
pub fn is_empty(self: &Self, x: u32) -> bool {
pub fn is_empty(&self, x: u32) -> bool {
false
}
}
pub trait WithIsEmpty {
fn len(self: &Self) -> isize;
fn is_empty(self: &Self) -> bool;
fn len(&self) -> isize;
fn is_empty(&self) -> bool;
}
impl WithIsEmpty for Wither {
fn len(self: &Self) -> isize {
fn len(&self) -> isize {
1
}
fn is_empty(self: &Self) -> bool {
fn is_empty(&self) -> bool {
false
}
}

View file

@ -7,12 +7,12 @@ pub struct One;
struct Wither;
trait TraitsToo {
fn len(self: &Self) -> isize;
fn len(&self) -> isize;
// No error; `len` is private; see issue #1085.
}
impl TraitsToo for One {
fn len(self: &Self) -> isize {
fn len(&self) -> isize {
0
}
}
@ -20,11 +20,11 @@ impl TraitsToo for One {
pub struct HasIsEmpty;
impl HasIsEmpty {
pub fn len(self: &Self) -> isize {
pub fn len(&self) -> isize {
1
}
fn is_empty(self: &Self) -> bool {
fn is_empty(&self) -> bool {
false
}
}
@ -32,26 +32,26 @@ impl HasIsEmpty {
pub struct HasWrongIsEmpty;
impl HasWrongIsEmpty {
pub fn len(self: &Self) -> isize {
pub fn len(&self) -> isize {
1
}
pub fn is_empty(self: &Self, x: u32) -> bool {
pub fn is_empty(&self, x: u32) -> bool {
false
}
}
pub trait WithIsEmpty {
fn len(self: &Self) -> isize;
fn is_empty(self: &Self) -> bool;
fn len(&self) -> isize;
fn is_empty(&self) -> bool;
}
impl WithIsEmpty for Wither {
fn len(self: &Self) -> isize {
fn len(&self) -> isize {
1
}
fn is_empty(self: &Self) -> bool {
fn is_empty(&self) -> bool {
false
}
}

View file

@ -43,10 +43,6 @@ impl S {
42
}
async fn meth_fut(&self) -> i32 { 42 }
async fn empty_fut(&self) {}
// should be ignored
fn not_fut(&self) -> i32 {
42
@ -64,4 +60,40 @@ impl S {
}
}
// Tests related to lifetime capture
async fn elided(_: &i32) -> i32 { 42 }
// should be ignored
fn elided_not_bound(_: &i32) -> impl Future<Output = i32> {
async { 42 }
}
async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 }
// should be ignored
#[allow(clippy::needless_lifetimes)]
fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> {
async { 42 }
}
// should be ignored
mod issue_5765 {
use std::future::Future;
struct A;
impl A {
fn f(&self) -> impl Future<Output = ()> {
async {}
}
}
fn test() {
let _future = {
let a = A;
a.f()
};
}
}
fn main() {}

View file

@ -51,14 +51,6 @@ impl S {
}
}
fn meth_fut(&self) -> impl Future<Output = i32> {
async { 42 }
}
fn empty_fut(&self) -> impl Future<Output = ()> {
async {}
}
// should be ignored
fn not_fut(&self) -> i32 {
42
@ -76,4 +68,44 @@ impl S {
}
}
// Tests related to lifetime capture
fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
async { 42 }
}
// should be ignored
fn elided_not_bound(_: &i32) -> impl Future<Output = i32> {
async { 42 }
}
fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b {
async { 42 }
}
// should be ignored
#[allow(clippy::needless_lifetimes)]
fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> {
async { 42 }
}
// should be ignored
mod issue_5765 {
use std::future::Future;
struct A;
impl A {
fn f(&self) -> impl Future<Output = ()> {
async {}
}
}
fn test() {
let _future = {
let a = A;
a.f()
};
}
}
fn main() {}

View file

@ -65,34 +65,34 @@ LL | let c = 21;
...
error: this function can be simplified using the `async fn` syntax
--> $DIR/manual_async_fn.rs:54:5
--> $DIR/manual_async_fn.rs:73:1
|
LL | fn meth_fut(&self) -> impl Future<Output = i32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: make the function `async` and return the output of the future directly
|
LL | async fn meth_fut(&self) -> i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | async fn elided(_: &i32) -> i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: move the body of the async block to the enclosing function
|
LL | fn meth_fut(&self) -> impl Future<Output = i32> { 42 }
| ^^^^^^
LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ { 42 }
| ^^^^^^
error: this function can be simplified using the `async fn` syntax
--> $DIR/manual_async_fn.rs:58:5
--> $DIR/manual_async_fn.rs:82:1
|
LL | fn empty_fut(&self) -> impl Future<Output = ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: make the function `async` and remove the return type
help: make the function `async` and return the output of the future directly
|
LL | async fn empty_fut(&self) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
LL | async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: move the body of the async block to the enclosing function
|
LL | fn empty_fut(&self) -> impl Future<Output = ()> {}
| ^^
LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b { 42 }
| ^^^^^^
error: aborting due to 6 previous errors

View file

@ -5,6 +5,20 @@
#![allow(clippy::map_identity)]
fn main() {
// mapping to Option on Iterator
fn option_id(x: i8) -> Option<i8> {
Some(x)
}
let option_id_ref: fn(i8) -> Option<i8> = option_id;
let option_id_closure = |x| Some(x);
let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect();
let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect();
let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect();
let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect();
// mapping to Iterator on Iterator
let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
// mapping to Option on Option
let _: Option<_> = (Some(Some(1))).and_then(|x| x);
}

View file

@ -5,6 +5,20 @@
#![allow(clippy::map_identity)]
fn main() {
// mapping to Option on Iterator
fn option_id(x: i8) -> Option<i8> {
Some(x)
}
let option_id_ref: fn(i8) -> Option<i8> = option_id;
let option_id_closure = |x| Some(x);
let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
// mapping to Iterator on Iterator
let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
// mapping to Option on Option
let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
}

View file

@ -1,16 +1,40 @@
error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)`
--> $DIR/map_flatten.rs:8:21
error: called `map(..).flatten()` on an `Iterator`
--> $DIR/map_flatten.rs:14:46
|
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)`
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)`
|
= note: `-D clippy::map-flatten` implied by `-D warnings`
error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)`
--> $DIR/map_flatten.rs:9:24
error: called `map(..).flatten()` on an `Iterator`
--> $DIR/map_flatten.rs:15:46
|
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)`
error: called `map(..).flatten()` on an `Iterator`
--> $DIR/map_flatten.rs:16:46
|
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)`
error: called `map(..).flatten()` on an `Iterator`
--> $DIR/map_flatten.rs:17:46
|
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))`
error: called `map(..).flatten()` on an `Iterator`
--> $DIR/map_flatten.rs:20:46
|
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)`
error: called `map(..).flatten()` on an `Option`
--> $DIR/map_flatten.rs:23:39
|
LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)`
| ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
error: aborting due to 2 previous errors
error: aborting due to 6 previous errors

View file

@ -6,6 +6,18 @@ use std::cmp::{max, min};
const LARGE: usize = 3;
struct NotOrd(u64);
impl NotOrd {
fn min(self, x: u64) -> NotOrd {
NotOrd(x)
}
fn max(self, x: u64) -> NotOrd {
NotOrd(x)
}
}
fn main() {
let x;
x = 2usize;
@ -30,4 +42,24 @@ fn main() {
max(min(s, "Apple"), "Zoo");
max("Apple", min(s, "Zoo")); // ok
let f = 3f32;
x.min(1).max(3);
x.max(3).min(1);
f.max(3f32).min(1f32);
x.max(1).min(3); // ok
x.min(3).max(1); // ok
f.min(3f32).max(1f32); // ok
max(x.min(1), 3);
min(x.max(1), 3); // ok
s.max("Zoo").min("Apple");
s.min("Apple").max("Zoo");
s.min("Zoo").max("Apple"); // ok
let not_ord = NotOrd(1);
not_ord.min(1).max(3); // ok
}

View file

@ -1,5 +1,5 @@
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:12:5
--> $DIR/min_max.rs:24:5
|
LL | min(1, max(3, x));
| ^^^^^^^^^^^^^^^^^
@ -7,40 +7,76 @@ LL | min(1, max(3, x));
= note: `-D clippy::min-max` implied by `-D warnings`
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:13:5
--> $DIR/min_max.rs:25:5
|
LL | min(max(3, x), 1);
| ^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:14:5
--> $DIR/min_max.rs:26:5
|
LL | max(min(x, 1), 3);
| ^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:15:5
--> $DIR/min_max.rs:27:5
|
LL | max(3, min(x, 1));
| ^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:17:5
--> $DIR/min_max.rs:29:5
|
LL | my_max(3, my_min(x, 1));
| ^^^^^^^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:29:5
--> $DIR/min_max.rs:41:5
|
LL | min("Apple", max("Zoo", s));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:30:5
--> $DIR/min_max.rs:42:5
|
LL | max(min(s, "Apple"), "Zoo");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 7 previous errors
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:47:5
|
LL | x.min(1).max(3);
| ^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:48:5
|
LL | x.max(3).min(1);
| ^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:49:5
|
LL | f.max(3f32).min(1f32);
| ^^^^^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:55:5
|
LL | max(x.min(1), 3);
| ^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:58:5
|
LL | s.max("Zoo").min("Apple");
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
--> $DIR/min_max.rs:59:5
|
LL | s.min("Apple").max("Zoo");
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 13 previous errors

View file

@ -0,0 +1,69 @@
// run-rustfix
#![warn(clippy::needless_arbitrary_self_type)]
#![allow(unused_mut, clippy::needless_lifetimes)]
pub enum ValType {
A,
B,
}
impl ValType {
pub fn bad(self) {
unimplemented!();
}
pub fn good(self) {
unimplemented!();
}
pub fn mut_bad(mut self) {
unimplemented!();
}
pub fn mut_good(mut self) {
unimplemented!();
}
pub fn ref_bad(&self) {
unimplemented!();
}
pub fn ref_good(&self) {
unimplemented!();
}
pub fn ref_bad_with_lifetime<'a>(&'a self) {
unimplemented!();
}
pub fn ref_good_with_lifetime<'a>(&'a self) {
unimplemented!();
}
pub fn mut_ref_bad(&mut self) {
unimplemented!();
}
pub fn mut_ref_good(&mut self) {
unimplemented!();
}
pub fn mut_ref_bad_with_lifetime<'a>(&'a mut self) {
unimplemented!();
}
pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) {
unimplemented!();
}
pub fn mut_ref_mut_good(mut self: &mut Self) {
unimplemented!();
}
pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) {
unimplemented!();
}
}
fn main() {}

View file

@ -0,0 +1,69 @@
// run-rustfix
#![warn(clippy::needless_arbitrary_self_type)]
#![allow(unused_mut, clippy::needless_lifetimes)]
pub enum ValType {
A,
B,
}
impl ValType {
pub fn bad(self: Self) {
unimplemented!();
}
pub fn good(self) {
unimplemented!();
}
pub fn mut_bad(mut self: Self) {
unimplemented!();
}
pub fn mut_good(mut self) {
unimplemented!();
}
pub fn ref_bad(self: &Self) {
unimplemented!();
}
pub fn ref_good(&self) {
unimplemented!();
}
pub fn ref_bad_with_lifetime<'a>(self: &'a Self) {
unimplemented!();
}
pub fn ref_good_with_lifetime<'a>(&'a self) {
unimplemented!();
}
pub fn mut_ref_bad(self: &mut Self) {
unimplemented!();
}
pub fn mut_ref_good(&mut self) {
unimplemented!();
}
pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) {
unimplemented!();
}
pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) {
unimplemented!();
}
pub fn mut_ref_mut_good(mut self: &mut Self) {
unimplemented!();
}
pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) {
unimplemented!();
}
}
fn main() {}

View file

@ -0,0 +1,40 @@
error: the type of the `self` parameter does not need to be arbitrary
--> $DIR/needless_arbitrary_self_type.rs:12:16
|
LL | pub fn bad(self: Self) {
| ^^^^^^^^^^ help: consider to change this parameter to: `self`
|
= note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings`
error: the type of the `self` parameter does not need to be arbitrary
--> $DIR/needless_arbitrary_self_type.rs:20:20
|
LL | pub fn mut_bad(mut self: Self) {
| ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self`
error: the type of the `self` parameter does not need to be arbitrary
--> $DIR/needless_arbitrary_self_type.rs:28:20
|
LL | pub fn ref_bad(self: &Self) {
| ^^^^^^^^^^^ help: consider to change this parameter to: `&self`
error: the type of the `self` parameter does not need to be arbitrary
--> $DIR/needless_arbitrary_self_type.rs:36:38
|
LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) {
| ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self`
error: the type of the `self` parameter does not need to be arbitrary
--> $DIR/needless_arbitrary_self_type.rs:44:24
|
LL | pub fn mut_ref_bad(self: &mut Self) {
| ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self`
error: the type of the `self` parameter does not need to be arbitrary
--> $DIR/needless_arbitrary_self_type.rs:52:42
|
LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) {
| ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self`
error: aborting due to 6 previous errors

View file

@ -5,11 +5,11 @@
use std::collections::{BTreeSet, HashMap, HashSet};
#[warn(clippy::needless_collect)]
#[allow(unused_variables, clippy::iter_cloned_collect)]
#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)]
fn main() {
let sample = [1; 5];
let len = sample.iter().count();
if sample.get(0).is_none() {
if sample.iter().next().is_none() {
// Empty
}
sample.iter().cloned().any(|x| x == 1);

View file

@ -5,7 +5,7 @@
use std::collections::{BTreeSet, HashMap, HashSet};
#[warn(clippy::needless_collect)]
#[allow(unused_variables, clippy::iter_cloned_collect)]
#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)]
fn main() {
let sample = [1; 5];
let len = sample.iter().collect::<Vec<_>>().len();

View file

@ -7,10 +7,10 @@ LL | let len = sample.iter().collect::<Vec<_>>().len();
= note: `-D clippy::needless-collect` implied by `-D warnings`
error: avoid using `collect()` when not needed
--> $DIR/needless_collect.rs:12:15
--> $DIR/needless_collect.rs:12:22
|
LL | if sample.iter().collect::<Vec<_>>().is_empty() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get(0).is_none()`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
error: avoid using `collect()` when not needed
--> $DIR/needless_collect.rs:15:28

View file

@ -0,0 +1,19 @@
use std::collections::{HashMap, VecDeque};
fn main() {
let sample = [1; 5];
let indirect_iter = sample.iter().collect::<Vec<_>>();
indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
let indirect_len = sample.iter().collect::<VecDeque<_>>();
indirect_len.len();
let indirect_empty = sample.iter().collect::<VecDeque<_>>();
indirect_empty.is_empty();
let indirect_contains = sample.iter().collect::<VecDeque<_>>();
indirect_contains.contains(&&5);
let indirect_negative = sample.iter().collect::<Vec<_>>();
indirect_negative.len();
indirect_negative
.into_iter()
.map(|x| (*x, *x + 1))
.collect::<HashMap<_, _>>();
}

View file

@ -0,0 +1,55 @@
error: avoid using `collect()` when not needed
--> $DIR/needless_collect_indirect.rs:5:5
|
LL | / let indirect_iter = sample.iter().collect::<Vec<_>>();
LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
| |____^
|
= note: `-D clippy::needless-collect` implied by `-D warnings`
help: Use the original Iterator instead of collecting it and then producing a new one
|
LL |
LL | sample.iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
|
error: avoid using `collect()` when not needed
--> $DIR/needless_collect_indirect.rs:7:5
|
LL | / let indirect_len = sample.iter().collect::<VecDeque<_>>();
LL | | indirect_len.len();
| |____^
|
help: Take the original Iterator's count instead of collecting it and finding the length
|
LL |
LL | sample.iter().count();
|
error: avoid using `collect()` when not needed
--> $DIR/needless_collect_indirect.rs:9:5
|
LL | / let indirect_empty = sample.iter().collect::<VecDeque<_>>();
LL | | indirect_empty.is_empty();
| |____^
|
help: Check if the original Iterator has anything instead of collecting it and seeing if it's empty
|
LL |
LL | sample.iter().next().is_none();
|
error: avoid using `collect()` when not needed
--> $DIR/needless_collect_indirect.rs:11:5
|
LL | / let indirect_contains = sample.iter().collect::<VecDeque<_>>();
LL | | indirect_contains.contains(&&5);
| |____^
|
help: Check if the original Iterator contains an element instead of collecting then checking
|
LL |
LL | sample.iter().any(|x| x == &&5);
|
error: aborting due to 4 previous errors

View file

@ -1,4 +1,4 @@
error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
--> $DIR/neg_cmp_op_on_partial_ord.rs:16:21
|
LL | let _not_less = !(a_value < another_value);
@ -6,19 +6,19 @@ LL | let _not_less = !(a_value < another_value);
|
= note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings`
error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
--> $DIR/neg_cmp_op_on_partial_ord.rs:19:30
|
LL | let _not_less_or_equal = !(a_value <= another_value);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
--> $DIR/neg_cmp_op_on_partial_ord.rs:22:24
|
LL | let _not_greater = !(a_value > another_value);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
--> $DIR/neg_cmp_op_on_partial_ord.rs:25:33
|
LL | let _not_greater_or_equal = !(a_value >= another_value);

View file

@ -1,4 +1,4 @@
error: Negation by multiplying with `-1`
error: negation by multiplying with `-1`
--> $DIR/neg_multiply.rs:27:5
|
LL | x * -1;
@ -6,7 +6,7 @@ LL | x * -1;
|
= note: `-D clippy::neg-multiply` implied by `-D warnings`
error: Negation by multiplying with `-1`
error: negation by multiplying with `-1`
--> $DIR/neg_multiply.rs:29:5
|
LL | -1 * x;

View file

@ -22,9 +22,9 @@ struct HasOption {
}
impl HasOption {
fn do_option_nothing(self: &Self, value: usize) {}
fn do_option_nothing(&self, value: usize) {}
fn do_option_plus_one(self: &Self, value: usize) -> usize {
fn do_option_plus_one(&self, value: usize) -> usize {
value + 1
}
}

View file

@ -22,9 +22,9 @@ struct HasOption {
}
impl HasOption {
fn do_option_nothing(self: &Self, value: usize) {}
fn do_option_nothing(&self, value: usize) {}
fn do_option_plus_one(self: &Self, value: usize) -> usize {
fn do_option_plus_one(&self, value: usize) -> usize {
value + 1
}
}

View file

@ -116,4 +116,12 @@ fn f() -> Option<()> {
Some(())
}
// Issue 5886 - const fn (with no arguments)
pub fn skip_const_fn_with_no_args() {
const fn foo() -> Option<i32> {
Some(42)
}
let _ = None.or(foo());
}
fn main() {}

View file

@ -116,4 +116,12 @@ fn f() -> Option<()> {
Some(())
}
// Issue 5886 - const fn (with no arguments)
pub fn skip_const_fn_with_no_args() {
const fn foo() -> Option<i32> {
Some(42)
}
let _ = None.or(foo());
}
fn main() {}

View file

@ -1,4 +1,4 @@
error: You are trying to use classic C overflow conditions that will fail in Rust.
error: you are trying to use classic C overflow conditions that will fail in Rust
--> $DIR/overflow_check_conditional.rs:8:8
|
LL | if a + b < a {}
@ -6,43 +6,43 @@ LL | if a + b < a {}
|
= note: `-D clippy::overflow-check-conditional` implied by `-D warnings`
error: You are trying to use classic C overflow conditions that will fail in Rust.
error: you are trying to use classic C overflow conditions that will fail in Rust
--> $DIR/overflow_check_conditional.rs:9:8
|
LL | if a > a + b {}
| ^^^^^^^^^
error: You are trying to use classic C overflow conditions that will fail in Rust.
error: you are trying to use classic C overflow conditions that will fail in Rust
--> $DIR/overflow_check_conditional.rs:10:8
|
LL | if a + b < b {}
| ^^^^^^^^^
error: You are trying to use classic C overflow conditions that will fail in Rust.
error: you are trying to use classic C overflow conditions that will fail in Rust
--> $DIR/overflow_check_conditional.rs:11:8
|
LL | if b > a + b {}
| ^^^^^^^^^
error: You are trying to use classic C underflow conditions that will fail in Rust.
error: you are trying to use classic C underflow conditions that will fail in Rust
--> $DIR/overflow_check_conditional.rs:12:8
|
LL | if a - b > b {}
| ^^^^^^^^^
error: You are trying to use classic C underflow conditions that will fail in Rust.
error: you are trying to use classic C underflow conditions that will fail in Rust
--> $DIR/overflow_check_conditional.rs:13:8
|
LL | if b < a - b {}
| ^^^^^^^^^
error: You are trying to use classic C underflow conditions that will fail in Rust.
error: you are trying to use classic C underflow conditions that will fail in Rust
--> $DIR/overflow_check_conditional.rs:14:8
|
LL | if a - b > a {}
| ^^^^^^^^^
error: You are trying to use classic C underflow conditions that will fail in Rust.
error: you are trying to use classic C underflow conditions that will fail in Rust
--> $DIR/overflow_check_conditional.rs:15:8
|
LL | if a < a - b {}

View file

@ -1,4 +1,4 @@
error: Calling `push` with '/' or '/' (file system root) will overwrite the previous path definition
error: calling `push` with '/' or '/' (file system root) will overwrite the previous path definition
--> $DIR/path_buf_push_overwrite.rs:7:12
|
LL | x.push("/bar");

View file

@ -1,4 +1,4 @@
error: It is more idiomatic to use `v1.iter().enumerate()`
error: it is more idiomatic to use `v1.iter().enumerate()`
--> $DIR/range.rs:5:14
|
LL | let _x = v1.iter().zip(0..v1.len());

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