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

This commit is contained in:
flip1995 2020-10-25 16:43:53 +01:00
commit 77746b9060
No known key found for this signature in database
GPG key ID: 693086869D506637
57 changed files with 2076 additions and 393 deletions

View file

@ -1632,6 +1632,7 @@ Released 2018-09-13
[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async [`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map [`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
@ -1779,6 +1780,7 @@ Released 2018-09-13
[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups [`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant [`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
@ -1793,6 +1795,7 @@ Released 2018-09-13
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
@ -1841,6 +1844,7 @@ Released 2018-09-13
[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit [`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref [`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut [`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock
[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound [`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type [`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic [`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
@ -1936,6 +1940,7 @@ Released 2018-09-13
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`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 [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
@ -1979,6 +1984,7 @@ Released 2018-09-13
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init

View file

@ -45,13 +45,52 @@ declare_clippy_lint! {
/// } /// }
/// ``` /// ```
pub AWAIT_HOLDING_LOCK, pub AWAIT_HOLDING_LOCK,
pedantic, correctness,
"Inside an async function, holding a MutexGuard while calling await" "Inside an async function, holding a MutexGuard while calling await"
} }
declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]); declare_clippy_lint! {
/// **What it does:** Checks for calls to await while holding a
/// `RefCell` `Ref` or `RefMut`.
///
/// **Why is this bad?** `RefCell` refs only check for exclusive mutable access
/// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
/// risks panics from a mutable ref shared while other refs are outstanding.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust,ignore
/// use std::cell::RefCell;
///
/// async fn foo(x: &RefCell<u32>) {
/// let b = x.borrow_mut()();
/// *ref += 1;
/// bar.await;
/// }
/// ```
///
/// Use instead:
/// ```rust,ignore
/// use std::cell::RefCell;
///
/// async fn foo(x: &RefCell<u32>) {
/// {
/// let b = x.borrow_mut();
/// *ref += 1;
/// }
/// bar.await;
/// }
/// ```
pub AWAIT_HOLDING_REFCELL_REF,
correctness,
"Inside an async function, holding a RefCell ref while calling await"
}
impl LateLintPass<'_> for AwaitHoldingLock { declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]);
impl LateLintPass<'_> for AwaitHolding {
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
use AsyncGeneratorKind::{Block, Closure, Fn}; use AsyncGeneratorKind::{Block, Closure, Fn};
if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
@ -78,6 +117,16 @@ fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorType
"these are all the await points this lock is held through", "these are all the await points this lock is held through",
); );
} }
if is_refcell_ref(cx, adt.did) {
span_lint_and_note(
cx,
AWAIT_HOLDING_REFCELL_REF,
ty_cause.span,
"this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.",
ty_cause.scope_span.or(Some(span)),
"these are all the await points this ref is held through",
);
}
} }
} }
} }
@ -90,3 +139,7 @@ fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
} }
fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT)
}

View file

@ -579,9 +579,8 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet<
if let hir::PatKind::Wild = pat.kind { if let hir::PatKind::Wild = pat.kind {
return false; // ignore `_` patterns return false; // ignore `_` patterns
} }
let def_id = pat.hir_id.owner.to_def_id(); if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) {
if cx.tcx.has_typeck_results(def_id) { is_mutable_ty(cx, &cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys)
is_mutable_ty(cx, &cx.tcx.typeck(def_id.expect_local()).pat_ty(pat), pat.span, tys)
} else { } else {
false false
} }
@ -694,11 +693,10 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
Call(_, args) | MethodCall(_, _, args, _) => { Call(_, args) | MethodCall(_, _, args, _) => {
let mut tys = FxHashSet::default(); let mut tys = FxHashSet::default();
for arg in args { for arg in args {
let def_id = arg.hir_id.owner.to_def_id(); if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
if self.cx.tcx.has_typeck_results(def_id)
&& is_mutable_ty( && is_mutable_ty(
self.cx, self.cx,
self.cx.tcx.typeck(def_id.expect_local()).expr_ty(arg), self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg),
arg.span, arg.span,
&mut tys, &mut tys,
) )

View file

@ -92,13 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|db| { |db| {
cx.tcx.infer_ctxt().enter(|infcx| { cx.tcx.infer_ctxt().enter(|infcx| {
for FulfillmentError { obligation, .. } in send_errors { for FulfillmentError { obligation, .. } in send_errors {
infcx.maybe_note_obligation_cause_for_async_await( infcx.maybe_note_obligation_cause_for_async_await(db, &obligation);
db, if let Trait(trait_pred, _) = obligation.predicate.skip_binders() {
&obligation,
);
if let Trait(trait_pred, _) =
obligation.predicate.skip_binders()
{
db.note(&format!( db.note(&format!(
"`{}` doesn't implement `{}`", "`{}` doesn't implement `{}`",
trait_pred.self_ty(), trait_pred.self_ty(),

View file

@ -160,7 +160,7 @@ mod assign_ops;
mod async_yields_async; mod async_yields_async;
mod atomic_ordering; mod atomic_ordering;
mod attrs; mod attrs;
mod await_holding_lock; mod await_holding_invalid;
mod bit_mask; mod bit_mask;
mod blacklisted_name; mod blacklisted_name;
mod blocks_in_if_conditions; mod blocks_in_if_conditions;
@ -255,6 +255,7 @@ mod modulo_arithmetic;
mod multiple_crate_versions; mod multiple_crate_versions;
mod mut_key; mod mut_key;
mod mut_mut; mod mut_mut;
mod mut_mutex_lock;
mod mut_reference; mod mut_reference;
mod mutable_debug_assertion; mod mutable_debug_assertion;
mod mutex_atomic; mod mutex_atomic;
@ -278,6 +279,7 @@ mod overflow_check_conditional;
mod panic_in_result_fn; mod panic_in_result_fn;
mod panic_unimplemented; mod panic_unimplemented;
mod partialeq_ne_impl; mod partialeq_ne_impl;
mod pass_by_ref_or_value;
mod path_buf_push_overwrite; mod path_buf_push_overwrite;
mod pattern_type_mismatch; mod pattern_type_mismatch;
mod precedence; mod precedence;
@ -311,9 +313,9 @@ mod to_string_in_display;
mod trait_bounds; mod trait_bounds;
mod transmute; mod transmute;
mod transmuting_null; mod transmuting_null;
mod trivially_copy_pass_by_ref;
mod try_err; mod try_err;
mod types; mod types;
mod undropped_manually_drops;
mod unicode; mod unicode;
mod unit_return_expecting_ord; mod unit_return_expecting_ord;
mod unnamed_address; mod unnamed_address;
@ -509,7 +511,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&attrs::MISMATCHED_TARGET_OS, &attrs::MISMATCHED_TARGET_OS,
&attrs::UNKNOWN_CLIPPY_LINTS, &attrs::UNKNOWN_CLIPPY_LINTS,
&attrs::USELESS_ATTRIBUTE, &attrs::USELESS_ATTRIBUTE,
&await_holding_lock::AWAIT_HOLDING_LOCK, &await_holding_invalid::AWAIT_HOLDING_LOCK,
&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
&bit_mask::BAD_BIT_MASK, &bit_mask::BAD_BIT_MASK,
&bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK,
&bit_mask::VERBOSE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK,
@ -633,6 +636,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&loops::NEEDLESS_RANGE_LOOP, &loops::NEEDLESS_RANGE_LOOP,
&loops::NEVER_LOOP, &loops::NEVER_LOOP,
&loops::SAME_ITEM_PUSH, &loops::SAME_ITEM_PUSH,
&loops::SINGLE_ELEMENT_LOOP,
&loops::WHILE_IMMUTABLE_CONDITION, &loops::WHILE_IMMUTABLE_CONDITION,
&loops::WHILE_LET_LOOP, &loops::WHILE_LET_LOOP,
&loops::WHILE_LET_ON_ITERATOR, &loops::WHILE_LET_ON_ITERATOR,
@ -743,6 +747,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, &multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
&mut_key::MUTABLE_KEY_TYPE, &mut_key::MUTABLE_KEY_TYPE,
&mut_mut::MUT_MUT, &mut_mut::MUT_MUT,
&mut_mutex_lock::MUT_MUTEX_LOCK,
&mut_reference::UNNECESSARY_MUT_PASSED, &mut_reference::UNNECESSARY_MUT_PASSED,
&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, &mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
&mutex_atomic::MUTEX_ATOMIC, &mutex_atomic::MUTEX_ATOMIC,
@ -776,6 +781,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&panic_unimplemented::UNIMPLEMENTED, &panic_unimplemented::UNIMPLEMENTED,
&panic_unimplemented::UNREACHABLE, &panic_unimplemented::UNREACHABLE,
&partialeq_ne_impl::PARTIALEQ_NE_IMPL, &partialeq_ne_impl::PARTIALEQ_NE_IMPL,
&pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
&pattern_type_mismatch::PATTERN_TYPE_MISMATCH, &pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
&precedence::PRECEDENCE, &precedence::PRECEDENCE,
@ -785,6 +792,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&ptr_eq::PTR_EQ, &ptr_eq::PTR_EQ,
&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
&question_mark::QUESTION_MARK, &question_mark::QUESTION_MARK,
&ranges::MANUAL_RANGE_CONTAINS,
&ranges::RANGE_MINUS_ONE, &ranges::RANGE_MINUS_ONE,
&ranges::RANGE_PLUS_ONE, &ranges::RANGE_PLUS_ONE,
&ranges::RANGE_ZIP_WITH_LEN, &ranges::RANGE_ZIP_WITH_LEN,
@ -835,7 +843,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&transmute::USELESS_TRANSMUTE, &transmute::USELESS_TRANSMUTE,
&transmute::WRONG_TRANSMUTE, &transmute::WRONG_TRANSMUTE,
&transmuting_null::TRANSMUTING_NULL, &transmuting_null::TRANSMUTING_NULL,
&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF,
&try_err::TRY_ERR, &try_err::TRY_ERR,
&types::ABSURD_EXTREME_COMPARISONS, &types::ABSURD_EXTREME_COMPARISONS,
&types::BORROWED_BOX, &types::BORROWED_BOX,
@ -862,6 +869,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&types::UNIT_CMP, &types::UNIT_CMP,
&types::UNNECESSARY_CAST, &types::UNNECESSARY_CAST,
&types::VEC_BOX, &types::VEC_BOX,
&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
&unicode::INVISIBLE_CHARACTERS, &unicode::INVISIBLE_CHARACTERS,
&unicode::NON_ASCII_LITERAL, &unicode::NON_ASCII_LITERAL,
&unicode::UNICODE_NOT_NFC, &unicode::UNICODE_NOT_NFC,
@ -905,7 +913,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
]); ]);
// end register lints, do not remove this comment, its used in `update_lints` // end register lints, do not remove this comment, its used in `update_lints`
store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock); store.register_late_pass(|| box await_holding_invalid::AwaitHolding);
store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box serde_api::SerdeAPI);
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
@ -1009,11 +1017,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || box large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)); store.register_late_pass(move || box large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold));
store.register_late_pass(|| box explicit_write::ExplicitWrite); store.register_late_pass(|| box explicit_write::ExplicitWrite);
store.register_late_pass(|| box needless_pass_by_value::NeedlessPassByValue); store.register_late_pass(|| box needless_pass_by_value::NeedlessPassByValue);
let trivially_copy_pass_by_ref = trivially_copy_pass_by_ref::TriviallyCopyPassByRef::new( let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new(
conf.trivial_copy_size_limit, conf.trivial_copy_size_limit,
conf.pass_by_value_size_limit,
&sess.target, &sess.target,
); );
store.register_late_pass(move || box trivially_copy_pass_by_ref); store.register_late_pass(move || box pass_by_ref_or_value);
store.register_late_pass(|| box try_err::TryErr); store.register_late_pass(|| box try_err::TryErr);
store.register_late_pass(|| box use_self::UseSelf); store.register_late_pass(|| box use_self::UseSelf);
store.register_late_pass(|| box bytecount::ByteCount); store.register_late_pass(|| box bytecount::ByteCount);
@ -1109,6 +1118,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box future_not_send::FutureNotSend);
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box if_let_mutex::IfLetMutex);
store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
@ -1137,6 +1147,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));
store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax);
store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax);
store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
@ -1188,7 +1199,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&attrs::INLINE_ALWAYS),
LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK),
LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&bit_mask::VERBOSE_BIT_MASK),
LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
LintId::of(&copies::MATCH_SAME_ARMS), LintId::of(&copies::MATCH_SAME_ARMS),
@ -1237,13 +1247,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
LintId::of(&non_expressive_names::SIMILAR_NAMES), LintId::of(&non_expressive_names::SIMILAR_NAMES),
LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE),
LintId::of(&pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_MINUS_ONE),
LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&ranges::RANGE_PLUS_ONE),
LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&shadow::SHADOW_UNRELATED),
LintId::of(&strings::STRING_ADD_ASSIGN), LintId::of(&strings::STRING_ADD_ASSIGN),
LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF),
LintId::of(&types::CAST_LOSSLESS), LintId::of(&types::CAST_LOSSLESS),
LintId::of(&types::CAST_POSSIBLE_TRUNCATION), LintId::of(&types::CAST_POSSIBLE_TRUNCATION),
LintId::of(&types::CAST_POSSIBLE_WRAP), LintId::of(&types::CAST_POSSIBLE_WRAP),
@ -1287,6 +1298,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::MISMATCHED_TARGET_OS),
LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
LintId::of(&attrs::USELESS_ATTRIBUTE), LintId::of(&attrs::USELESS_ATTRIBUTE),
LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK),
LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::BAD_BIT_MASK),
LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),
LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blacklisted_name::BLACKLISTED_NAME),
@ -1363,6 +1376,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&loops::NEEDLESS_RANGE_LOOP), LintId::of(&loops::NEEDLESS_RANGE_LOOP),
LintId::of(&loops::NEVER_LOOP), LintId::of(&loops::NEVER_LOOP),
LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&loops::SAME_ITEM_PUSH),
LintId::of(&loops::SINGLE_ELEMENT_LOOP),
LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_LOOP),
LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&loops::WHILE_LET_ON_ITERATOR),
@ -1441,6 +1455,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN),
LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL),
LintId::of(&mut_key::MUTABLE_KEY_TYPE), LintId::of(&mut_key::MUTABLE_KEY_TYPE),
LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(&mutex_atomic::MUTEX_ATOMIC), LintId::of(&mutex_atomic::MUTEX_ATOMIC),
LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
@ -1469,6 +1484,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&ptr_eq::PTR_EQ), LintId::of(&ptr_eq::PTR_EQ),
LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
LintId::of(&question_mark::QUESTION_MARK), LintId::of(&question_mark::QUESTION_MARK),
LintId::of(&ranges::MANUAL_RANGE_CONTAINS),
LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&ranges::REVERSED_EMPTY_RANGES),
LintId::of(&redundant_clone::REDUNDANT_CLONE), LintId::of(&redundant_clone::REDUNDANT_CLONE),
@ -1521,6 +1537,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::UNIT_CMP), LintId::of(&types::UNIT_CMP),
LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::UNNECESSARY_CAST),
LintId::of(&types::VEC_BOX), LintId::of(&types::VEC_BOX),
LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unicode::INVISIBLE_CHARACTERS),
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
@ -1612,6 +1629,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS),
LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&misc_early::REDUNDANT_PATTERN),
LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&neg_multiply::NEG_MULTIPLY),
LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT),
@ -1624,6 +1642,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&ptr::PTR_ARG), LintId::of(&ptr::PTR_ARG),
LintId::of(&ptr_eq::PTR_EQ), LintId::of(&ptr_eq::PTR_EQ),
LintId::of(&question_mark::QUESTION_MARK), LintId::of(&question_mark::QUESTION_MARK),
LintId::of(&ranges::MANUAL_RANGE_CONTAINS),
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(&regex::TRIVIAL_REGEX), LintId::of(&regex::TRIVIAL_REGEX),
@ -1664,6 +1683,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::MUT_RANGE_BOUND),
LintId::of(&loops::SINGLE_ELEMENT_LOOP),
LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_LOOP),
LintId::of(&manual_strip::MANUAL_STRIP), LintId::of(&manual_strip::MANUAL_STRIP),
LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR),
@ -1733,6 +1753,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::DEPRECATED_SEMVER),
LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::MISMATCHED_TARGET_OS),
LintId::of(&attrs::USELESS_ATTRIBUTE), LintId::of(&attrs::USELESS_ATTRIBUTE),
LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK),
LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::BAD_BIT_MASK),
LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),
LintId::of(&booleans::LOGIC_BUG), LintId::of(&booleans::LOGIC_BUG),
@ -1790,6 +1812,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::CAST_REF_TO_MUT),
LintId::of(&types::UNIT_CMP), LintId::of(&types::UNIT_CMP),
LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unicode::INVISIBLE_CHARACTERS),
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),

View file

@ -414,7 +414,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
let mut visitor = RefVisitor::new(cx); let mut visitor = RefVisitor::new(cx);
// walk the type F, it may not contain LT refs // walk the type F, it may not contain LT refs
walk_ty(&mut visitor, &pred.bounded_ty); walk_ty(&mut visitor, &pred.bounded_ty);
if !visitor.lts.is_empty() { if !visitor.all_lts().is_empty() {
return true; return true;
} }
// if the bounds define new lifetimes, they are fine to occur // if the bounds define new lifetimes, they are fine to occur
@ -424,7 +424,9 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
walk_param_bound(&mut visitor, bound); walk_param_bound(&mut visitor, bound);
} }
// and check that all lifetimes are allowed // and check that all lifetimes are allowed
return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)); if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) {
return true;
}
}, },
WherePredicate::EqPredicate(ref pred) => { WherePredicate::EqPredicate(ref pred) => {
let mut visitor = RefVisitor::new(cx); let mut visitor = RefVisitor::new(cx);

View file

@ -4,9 +4,10 @@ use crate::utils::sugg::Sugg;
use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::usage::{is_unused, mutated_variables};
use crate::utils::{ use crate::utils::{
contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, contains_name, 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, is_type_diagnostic_item, last_path_segment, match_trait_method, indent_of, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment,
match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability, snippet_with_macro_callsite, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, snippet,
span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg,
span_lint_and_then, sugg, SpanlessEq,
}; };
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast; use rustc_ast::ast;
@ -293,9 +294,24 @@ declare_clippy_lint! {
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for empty `loop` expressions. /// **What it does:** Checks for empty `loop` expressions.
/// ///
/// **Why is this bad?** Those busy loops burn CPU cycles without doing /// **Why is this bad?** These busy loops burn CPU cycles without doing
/// anything. Think of the environment and either block on something or at least /// anything. It is _almost always_ a better idea to `panic!` than to have
/// make the thread sleep for some microseconds. /// a busy loop.
///
/// If panicking isn't possible, think of the environment and either:
/// - block on something
/// - sleep the thread for some microseconds
/// - yield or pause the thread
///
/// For `std` targets, this can be done with
/// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)
/// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).
///
/// For `no_std` targets, doing this is more complicated, especially because
/// `#[panic_handler]`s can't panic. To stop/pause the thread, you will
/// probably need to invoke some target-specific intrinsic. Examples include:
/// - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)
/// - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)
/// ///
/// **Known problems:** None. /// **Known problems:** None.
/// ///
@ -452,6 +468,31 @@ declare_clippy_lint! {
"the same item is pushed inside of a for loop" "the same item is pushed inside of a for loop"
} }
declare_clippy_lint! {
/// **What it does:** Checks whether a for loop has a single element.
///
/// **Why is this bad?** There is no reason to have a loop of a
/// single element.
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// let item1 = 2;
/// for item in &[item1] {
/// println!("{}", item);
/// }
/// ```
/// could be written as
/// ```rust
/// let item1 = 2;
/// let item = &item1;
/// println!("{}", item);
/// ```
pub SINGLE_ELEMENT_LOOP,
complexity,
"there is no reason to have a single element loop"
}
declare_lint_pass!(Loops => [ declare_lint_pass!(Loops => [
MANUAL_MEMCPY, MANUAL_MEMCPY,
NEEDLESS_RANGE_LOOP, NEEDLESS_RANGE_LOOP,
@ -469,6 +510,7 @@ declare_lint_pass!(Loops => [
MUT_RANGE_BOUND, MUT_RANGE_BOUND,
WHILE_IMMUTABLE_CONDITION, WHILE_IMMUTABLE_CONDITION,
SAME_ITEM_PUSH, SAME_ITEM_PUSH,
SINGLE_ELEMENT_LOOP,
]); ]);
impl<'tcx> LateLintPass<'tcx> for Loops { impl<'tcx> LateLintPass<'tcx> for Loops {
@ -502,13 +544,15 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
// (even if the "match" or "if let" is used for declaration) // (even if the "match" or "if let" is used for declaration)
if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind { if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind {
// also check for empty `loop {}` statements // also check for empty `loop {}` statements
// TODO(issue #6161): Enable for no_std crates (outside of #[panic_handler])
if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) { if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) {
span_lint( span_lint_and_help(
cx, cx,
EMPTY_LOOP, EMPTY_LOOP,
expr.span, expr.span,
"empty `loop {}` detected. You may want to either use `panic!()` or add \ "empty `loop {}` wastes CPU cycles",
`std::thread::sleep(..);` to the loop body.", None,
"You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.",
); );
} }
@ -777,6 +821,7 @@ fn check_for_loop<'tcx>(
check_for_loop_arg(cx, pat, arg, expr); check_for_loop_arg(cx, pat, arg, expr);
check_for_loop_over_map_kv(cx, pat, arg, body, expr); check_for_loop_over_map_kv(cx, pat, arg, body, expr);
check_for_mut_range_bound(cx, arg, body); check_for_mut_range_bound(cx, arg, body);
check_for_single_element_loop(cx, pat, arg, body, expr);
detect_same_item_push(cx, pat, arg, body, expr); detect_same_item_push(cx, pat, arg, body, expr);
} }
@ -1866,6 +1911,43 @@ fn check_for_loop_over_map_kv<'tcx>(
} }
} }
fn check_for_single_element_loop<'tcx>(
cx: &LateContext<'tcx>,
pat: &'tcx Pat<'_>,
arg: &'tcx Expr<'_>,
body: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>,
) {
if_chain! {
if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg_expr) = arg.kind;
if let PatKind::Binding(.., target, _) = pat.kind;
if let ExprKind::Array(ref arg_expr_list) = arg_expr.kind;
if let [arg_expression] = arg_expr_list;
if let ExprKind::Path(ref list_item) = arg_expression.kind;
if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name);
if let ExprKind::Block(ref block, _) = body.kind;
if !block.stmts.is_empty();
then {
let for_span = get_span_of_entire_for_loop(expr);
let mut block_str = snippet(cx, block.span, "..").into_owned();
block_str.remove(0);
block_str.pop();
span_lint_and_sugg(
cx,
SINGLE_ELEMENT_LOOP,
for_span,
"for loop over a single element",
"try",
format!("{{\n{}let {} = &{};{}}}", " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)), target.name, list_item_name, block_str),
Applicability::MachineApplicable
)
}
}
}
struct MutatePairDelegate<'a, 'tcx> { struct MutatePairDelegate<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
hir_id_low: Option<HirId>, hir_id_low: Option<HirId>,
@ -1969,12 +2051,11 @@ fn check_for_mutation<'tcx>(
span_low: None, span_low: None,
span_high: None, span_high: None,
}; };
let def_id = body.hir_id.owner.to_def_id();
cx.tcx.infer_ctxt().enter(|infcx| { cx.tcx.infer_ctxt().enter(|infcx| {
ExprUseVisitor::new( ExprUseVisitor::new(
&mut delegate, &mut delegate,
&infcx, &infcx,
def_id.expect_local(), body.hir_id.owner,
cx.param_env, cx.param_env,
cx.typeck_results(), cx.typeck_results(),
) )

View file

@ -1,8 +1,9 @@
use crate::consts::constant_simple; use crate::consts::constant_simple;
use crate::utils; use crate::utils;
use crate::utils::sugg;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath}; use rustc_hir::{def, Arm, Expr, ExprKind, Pat, PatKind, QPath};
use rustc_lint::LintContext; use rustc_lint::LintContext;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
@ -10,7 +11,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** /// **What it does:**
/// Finds patterns that reimplement `Option::unwrap_or`. /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
/// ///
/// **Why is this bad?** /// **Why is this bad?**
/// Concise code helps focusing on behavior instead of boilerplate. /// Concise code helps focusing on behavior instead of boilerplate.
@ -33,7 +34,7 @@ declare_clippy_lint! {
/// ``` /// ```
pub MANUAL_UNWRAP_OR, pub MANUAL_UNWRAP_OR,
complexity, complexity,
"finds patterns that can be encoded more concisely with `Option::unwrap_or`" "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
} }
declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]);
@ -43,32 +44,50 @@ impl LateLintPass<'_> for ManualUnwrapOr {
if in_external_macro(cx.sess(), expr.span) { if in_external_macro(cx.sess(), expr.span) {
return; return;
} }
lint_option_unwrap_or_case(cx, expr); lint_manual_unwrap_or(cx, expr);
} }
} }
fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { #[derive(Copy, Clone)]
fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { enum Case {
Option,
Result,
}
impl Case {
fn unwrap_fn_path(&self) -> &str {
match self {
Case::Option => "Option::unwrap_or",
Case::Result => "Result::unwrap_or",
}
}
}
fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
if_chain! { if_chain! {
if arms.len() == 2; if arms.len() == 2;
if arms.iter().all(|arm| arm.guard.is_none()); if arms.iter().all(|arm| arm.guard.is_none());
if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)| if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)|
if let PatKind::Path(ref qpath) = arm.pat.kind { match arm.pat.kind {
utils::match_qpath(qpath, &utils::paths::OPTION_NONE) PatKind::Path(ref some_qpath) =>
} else { utils::match_qpath(some_qpath, &utils::paths::OPTION_NONE),
false PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) =>
utils::match_qpath(err_qpath, &utils::paths::RESULT_ERR),
_ => false,
} }
); );
let some_arm = &arms[1 - idx]; let unwrap_arm = &arms[1 - idx];
if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind; if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
if utils::match_qpath(some_qpath, &utils::paths::OPTION_SOME); if utils::match_qpath(unwrap_qpath, &utils::paths::OPTION_SOME)
if let PatKind::Binding(_, binding_hir_id, ..) = some_binding.kind; || utils::match_qpath(unwrap_qpath, &utils::paths::RESULT_OK);
if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind; if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
if let ExprKind::Path(QPath::Resolved(_, body_path)) = unwrap_arm.body.kind;
if let def::Res::Local(body_path_hir_id) = body_path.res; if let def::Res::Local(body_path_hir_id) = body_path.res;
if body_path_hir_id == binding_hir_id; if body_path_hir_id == binding_hir_id;
if !utils::usage::contains_return_break_continue_macro(none_arm.body); if !utils::usage::contains_return_break_continue_macro(or_arm.body);
then { then {
Some(none_arm) Some(or_arm)
} else { } else {
None None
} }
@ -78,24 +97,29 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc
if_chain! { if_chain! {
if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind;
let ty = cx.typeck_results().expr_ty(scrutinee); let ty = cx.typeck_results().expr_ty(scrutinee);
if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)) {
if let Some(none_arm) = applicable_none_arm(match_arms); Some(Case::Option)
if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); } else if utils::is_type_diagnostic_item(cx, ty, sym!(result_type)) {
if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); Some(Case::Result)
} else {
None
};
if let Some(or_arm) = applicable_or_arm(match_arms);
if let Some(or_body_snippet) = utils::snippet_opt(cx, or_arm.body.span);
if let Some(indent) = utils::indent_of(cx, expr.span); if let Some(indent) = utils::indent_of(cx, expr.span);
if constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
then { then {
let reindented_none_body = let reindented_or_body =
utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); utils::reindent_multiline(or_body_snippet.into(), true, Some(indent));
utils::span_lint_and_sugg( utils::span_lint_and_sugg(
cx, cx,
MANUAL_UNWRAP_OR, expr.span, MANUAL_UNWRAP_OR, expr.span,
"this pattern reimplements `Option::unwrap_or`", &format!("this pattern reimplements `{}`", case.unwrap_fn_path()),
"replace with", "replace with",
format!( format!(
"{}.unwrap_or({})", "{}.unwrap_or({})",
scrutinee_snippet, sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(),
reindented_none_body, reindented_or_body,
), ),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );

View file

@ -0,0 +1,68 @@
use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Mutability};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:** Checks for `&mut Mutex::lock` calls
///
/// **Why is this bad?** `Mutex::lock` is less efficient than
/// calling `Mutex::get_mut`. In addition you also have a statically
/// guarantee that the mutex isn't locked, instead of just a runtime
/// guarantee.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// use std::sync::{Arc, Mutex};
///
/// let mut value_rc = Arc::new(Mutex::new(42_u8));
/// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
///
/// let mut value = value_mutex.lock().unwrap();
/// *value += 1;
/// ```
/// Use instead:
/// ```rust
/// use std::sync::{Arc, Mutex};
///
/// let mut value_rc = Arc::new(Mutex::new(42_u8));
/// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
///
/// let value = value_mutex.get_mut().unwrap();
/// *value += 1;
/// ```
pub MUT_MUTEX_LOCK,
style,
"`&mut Mutex::lock` does unnecessary locking"
}
declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]);
impl<'tcx> LateLintPass<'tcx> for MutMutexLock {
fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
if_chain! {
if let ExprKind::MethodCall(path, method_span, args, _) = &ex.kind;
if path.ident.name == sym!(lock);
let ty = cx.typeck_results().expr_ty(&args[0]);
if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind();
if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type));
then {
span_lint_and_sugg(
cx,
MUT_MUTEX_LOCK,
*method_span,
"calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
"change this to",
"get_mut".to_owned(),
Applicability::MaybeIncorrect,
);
}
}
}
}

View file

@ -3,7 +3,9 @@
//! This lint is **warn** by default //! This lint is **warn** by default
use crate::utils::sugg::Sugg; use crate::utils::sugg::Sugg;
use crate::utils::{higher, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg}; use crate::utils::{
higher, is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg,
};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -233,6 +235,9 @@ fn check_comparison<'a, 'tcx>(
cx.typeck_results().expr_ty(left_side), cx.typeck_results().expr_ty(left_side),
cx.typeck_results().expr_ty(right_side), cx.typeck_results().expr_ty(right_side),
); );
if is_expn_of(left_side.span, "cfg").is_some() || is_expn_of(right_side.span, "cfg").is_some() {
return;
}
if l_ty.is_bool() && r_ty.is_bool() { if l_ty.is_bool() && r_ty.is_bool() {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
@ -295,7 +300,14 @@ fn suggest_bool_comparison<'a, 'tcx>(
message: &str, message: &str,
conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
) { ) {
let hint = Sugg::hir_with_applicability(cx, expr, "..", &mut applicability); let hint = if expr.span.from_expansion() {
if applicability != Applicability::Unspecified {
applicability = Applicability::MaybeIncorrect;
}
Sugg::hir_with_macro_callsite(cx, expr, "..")
} else {
Sugg::hir_with_applicability(cx, expr, "..", &mut applicability)
};
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
BOOL_COMPARISON, BOOL_COMPARISON,

View file

@ -0,0 +1,256 @@
use std::cmp;
use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_ast::attr;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
use rustc_target::abi::LayoutOf;
use rustc_target::spec::abi::Abi;
use rustc_target::spec::Target;
declare_clippy_lint! {
/// **What it does:** Checks for functions taking arguments by reference, where
/// the argument type is `Copy` and small enough to be more efficient to always
/// pass by value.
///
/// **Why is this bad?** In many calling conventions instances of structs will
/// be passed through registers if they fit into two or less general purpose
/// registers.
///
/// **Known problems:** This lint is target register size dependent, it is
/// limited to 32-bit to try and reduce portability problems between 32 and
/// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit
/// will be different.
///
/// The configuration option `trivial_copy_size_limit` can be set to override
/// this limit for a project.
///
/// This lint attempts to allow passing arguments by reference if a reference
/// to that argument is returned. This is implemented by comparing the lifetime
/// of the argument and return value for equality. However, this can cause
/// false positives in cases involving multiple lifetimes that are bounded by
/// each other.
///
/// **Example:**
///
/// ```rust
/// // Bad
/// fn foo(v: &u32) {}
/// ```
///
/// ```rust
/// // Better
/// fn foo(v: u32) {}
/// ```
pub TRIVIALLY_COPY_PASS_BY_REF,
pedantic,
"functions taking small copyable arguments by reference"
}
declare_clippy_lint! {
/// **What it does:** Checks for functions taking arguments by value, where
/// the argument type is `Copy` and large enough to be worth considering
/// passing by reference. Does not trigger if the function is being exported,
/// because that might induce API breakage, if the parameter is declared as mutable,
/// or if the argument is a `self`.
///
/// **Why is this bad?** Arguments passed by value might result in an unnecessary
/// shallow copy, taking up more space in the stack and requiring a call to
/// `memcpy`, which which can be expensive.
///
/// **Example:**
///
/// ```rust
/// #[derive(Clone, Copy)]
/// struct TooLarge([u8; 2048]);
///
/// // Bad
/// fn foo(v: TooLarge) {}
/// ```
/// ```rust
/// #[derive(Clone, Copy)]
/// struct TooLarge([u8; 2048]);
///
/// // Good
/// fn foo(v: &TooLarge) {}
/// ```
pub LARGE_TYPES_PASSED_BY_VALUE,
pedantic,
"functions taking large arguments by value"
}
#[derive(Copy, Clone)]
pub struct PassByRefOrValue {
ref_min_size: u64,
value_max_size: u64,
}
impl<'tcx> PassByRefOrValue {
pub fn new(ref_min_size: Option<u64>, value_max_size: u64, target: &Target) -> Self {
let ref_min_size = ref_min_size.unwrap_or_else(|| {
let bit_width = u64::from(target.pointer_width);
// Cap the calculated bit width at 32-bits to reduce
// portability problems between 32 and 64-bit targets
let bit_width = cmp::min(bit_width, 32);
#[allow(clippy::integer_division)]
let byte_width = bit_width / 8;
// Use a limit of 2 times the register byte width
byte_width * 2
});
Self {
ref_min_size,
value_max_size,
}
}
fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option<Span>) {
let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
let fn_sig = cx.tcx.fn_sig(fn_def_id);
let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id));
for (index, (input, &ty)) in decl.inputs.iter().zip(fn_sig.inputs()).enumerate() {
// All spans generated from a proc-macro invocation are the same...
match span {
Some(s) if s == input.span => return,
_ => (),
}
match ty.kind() {
ty::Ref(input_lt, ty, Mutability::Not) => {
// Use lifetimes to determine if we're returning a reference to the
// argument. In that case we can't switch to pass-by-value as the
// argument will not live long enough.
let output_lts = match *fn_sig.output().kind() {
ty::Ref(output_lt, _, _) => vec![output_lt],
ty::Adt(_, substs) => substs.regions().collect(),
_ => vec![],
};
if_chain! {
if !output_lts.contains(&input_lt);
if is_copy(cx, ty);
if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
if size <= self.ref_min_size;
if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind;
then {
let value_type = if is_self_ty(decl_ty) {
"self".into()
} else {
snippet(cx, decl_ty.span, "_").into()
};
span_lint_and_sugg(
cx,
TRIVIALLY_COPY_PASS_BY_REF,
input.span,
&format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size),
"consider passing by value instead",
value_type,
Applicability::Unspecified,
);
}
}
},
ty::Adt(_, _) | ty::Array(_, _) | ty::Tuple(_) => {
// if function has a body and parameter is annotated with mut, ignore
if let Some(param) = fn_body.and_then(|body| body.params.get(index)) {
match param.pat.kind {
PatKind::Binding(BindingAnnotation::Unannotated, _, _, _) => {},
_ => continue,
}
}
if_chain! {
if !cx.access_levels.is_exported(hir_id);
if is_copy(cx, ty);
if !is_self_ty(input);
if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
if size > self.value_max_size;
then {
span_lint_and_sugg(
cx,
LARGE_TYPES_PASSED_BY_VALUE,
input.span,
&format!("this argument ({} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", size, self.value_max_size),
"consider passing by reference instead",
format!("&{}", snippet(cx, input.span, "_")),
Applicability::MaybeIncorrect,
);
}
}
},
_ => {},
}
}
}
}
impl_lint_pass!(PassByRefOrValue => [TRIVIALLY_COPY_PASS_BY_REF, LARGE_TYPES_PASSED_BY_VALUE]);
impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
if item.span.from_expansion() {
return;
}
if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind {
self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None);
}
}
fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
kind: FnKind<'tcx>,
decl: &'tcx FnDecl<'_>,
_body: &'tcx Body<'_>,
span: Span,
hir_id: HirId,
) {
if span.from_expansion() {
return;
}
match kind {
FnKind::ItemFn(.., header, _, attrs) => {
if header.abi != Abi::Rust {
return;
}
for a in attrs {
if let Some(meta_items) = a.meta_item_list() {
if a.has_name(sym!(proc_macro_derive))
|| (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always)))
{
return;
}
}
}
},
FnKind::Method(..) => (),
FnKind::Closure(..) => return,
}
// Exclude non-inherent impls
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
ItemKind::Trait(..))
{
return;
}
}
self.check_poly_fn(cx, hir_id, decl, Some(span));
}
}

View file

@ -2,15 +2,19 @@ use crate::consts::{constant, Constant};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::RangeLimits; use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned; use rustc_span::source_map::{Span, Spanned};
use rustc_span::symbol::Ident;
use std::cmp::Ordering; use std::cmp::Ordering;
use crate::utils::sugg::Sugg; use crate::utils::sugg::Sugg;
use crate::utils::{get_parent_expr, is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then}; use crate::utils::{
get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability,
span_lint, span_lint_and_sugg, span_lint_and_then,
};
use crate::utils::{higher, SpanlessEq}; use crate::utils::{higher, SpanlessEq};
declare_clippy_lint! { declare_clippy_lint! {
@ -128,43 +132,51 @@ declare_clippy_lint! {
"reversing the limits of range expressions, resulting in empty ranges" "reversing the limits of range expressions, resulting in empty ranges"
} }
declare_clippy_lint! {
/// **What it does:** Checks for expressions like `x >= 3 && x < 8` that could
/// be more readably expressed as `(3..8).contains(x)`.
///
/// **Why is this bad?** `contains` expresses the intent better and has less
/// failure modes (such as fencepost errors or using `||` instead of `&&`).
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// // given
/// let x = 6;
///
/// assert!(x >= 3 && x < 8);
/// ```
/// Use instead:
/// ```rust
///# let x = 6;
/// assert!((3..8).contains(&x));
/// ```
pub MANUAL_RANGE_CONTAINS,
style,
"manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
}
declare_lint_pass!(Ranges => [ declare_lint_pass!(Ranges => [
RANGE_ZIP_WITH_LEN, RANGE_ZIP_WITH_LEN,
RANGE_PLUS_ONE, RANGE_PLUS_ONE,
RANGE_MINUS_ONE, RANGE_MINUS_ONE,
REVERSED_EMPTY_RANGES, REVERSED_EMPTY_RANGES,
MANUAL_RANGE_CONTAINS,
]); ]);
impl<'tcx> LateLintPass<'tcx> for Ranges { impl<'tcx> LateLintPass<'tcx> for Ranges {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind { match expr.kind {
let name = path.ident.as_str(); ExprKind::MethodCall(ref path, _, ref args, _) => {
if name == "zip" && args.len() == 2 { check_range_zip_with_len(cx, path, args, expr.span);
let iter = &args[0].kind; },
let zip_arg = &args[1]; ExprKind::Binary(ref op, ref l, ref r) => {
if_chain! { check_possible_range_contains(cx, op.node, l, r, expr.span);
// `.iter()` call },
if let ExprKind::MethodCall(ref iter_path, _, ref iter_args , _) = *iter; _ => {},
if iter_path.ident.name == sym!(iter);
// range expression in `.zip()` call: `0..x.len()`
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
if is_integer_const(cx, start, 0);
// `.len()` call
if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind;
if len_path.ident.name == sym!(len) && len_args.len() == 1;
// `.iter()` and `.len()` called on same `Path`
if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind;
if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind;
if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
then {
span_lint(cx,
RANGE_ZIP_WITH_LEN,
expr.span,
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
snippet(cx, iter_args[0].span, "_")));
}
}
}
} }
check_exclusive_range_plus_one(cx, expr); check_exclusive_range_plus_one(cx, expr);
@ -173,6 +185,148 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
} }
} }
fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) {
let combine_and = match op {
BinOpKind::And | BinOpKind::BitAnd => true,
BinOpKind::Or | BinOpKind::BitOr => false,
_ => return,
};
// value, name, order (higher/lower), inclusiveness
if let (Some((lval, lname, name_span, lval_span, lord, linc)), Some((rval, rname, _, rval_span, rord, rinc))) =
(check_range_bounds(cx, l), check_range_bounds(cx, r))
{
// we only lint comparisons on the same name and with different
// direction
if lname != rname || lord == rord {
return;
}
let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval);
if combine_and && ord == Some(rord) {
// order lower bound and upper bound
let (l_span, u_span, l_inc, u_inc) = if rord == Ordering::Less {
(lval_span, rval_span, linc, rinc)
} else {
(rval_span, lval_span, rinc, linc)
};
// we only lint inclusive lower bounds
if !l_inc {
return;
}
let (range_type, range_op) = if u_inc {
("RangeInclusive", "..=")
} else {
("Range", "..")
};
let mut applicability = Applicability::MachineApplicable;
let name = snippet_with_applicability(cx, name_span, "_", &mut applicability);
let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
span_lint_and_sugg(
cx,
MANUAL_RANGE_CONTAINS,
span,
&format!("manual `{}::contains` implementation", range_type),
"use",
format!("({}{}{}).contains(&{})", lo, range_op, hi, name),
applicability,
);
} else if !combine_and && ord == Some(lord) {
// `!_.contains(_)`
// order lower bound and upper bound
let (l_span, u_span, l_inc, u_inc) = if lord == Ordering::Less {
(lval_span, rval_span, linc, rinc)
} else {
(rval_span, lval_span, rinc, linc)
};
if l_inc {
return;
}
let (range_type, range_op) = if u_inc {
("Range", "..")
} else {
("RangeInclusive", "..=")
};
let mut applicability = Applicability::MachineApplicable;
let name = snippet_with_applicability(cx, name_span, "_", &mut applicability);
let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
span_lint_and_sugg(
cx,
MANUAL_RANGE_CONTAINS,
span,
&format!("manual `!{}::contains` implementation", range_type),
"use",
format!("!({}{}{}).contains(&{})", lo, range_op, hi, name),
applicability,
);
}
}
}
fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, Ident, Span, Span, Ordering, bool)> {
if let ExprKind::Binary(ref op, ref l, ref r) = ex.kind {
let (inclusive, ordering) = match op.node {
BinOpKind::Gt => (false, Ordering::Greater),
BinOpKind::Ge => (true, Ordering::Greater),
BinOpKind::Lt => (false, Ordering::Less),
BinOpKind::Le => (true, Ordering::Less),
_ => return None,
};
if let Some(id) = match_ident(l) {
if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
return Some((c, id, l.span, r.span, ordering, inclusive));
}
} else if let Some(id) = match_ident(r) {
if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
return Some((c, id, r.span, l.span, ordering.reverse(), inclusive));
}
}
}
None
}
fn match_ident(e: &Expr<'_>) -> Option<Ident> {
if let ExprKind::Path(ref qpath) = e.kind {
if let Some(seg) = single_segment_path(qpath) {
if seg.args.is_none() {
return Some(seg.ident);
}
}
}
None
}
fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
let name = path.ident.as_str();
if name == "zip" && args.len() == 2 {
let iter = &args[0].kind;
let zip_arg = &args[1];
if_chain! {
// `.iter()` call
if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = *iter;
if iter_path.ident.name == sym!(iter);
// range expression in `.zip()` call: `0..x.len()`
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
if is_integer_const(cx, start, 0);
// `.len()` call
if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind;
if len_path.ident.name == sym!(len) && len_args.len() == 1;
// `.iter()` and `.len()` called on same `Path`
if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind;
if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind;
if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
then {
span_lint(cx,
RANGE_ZIP_WITH_LEN,
span,
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
snippet(cx, iter_args[0].span, "_"))
);
}
}
}
}
// exclusive range plus one: `x..(y+1)` // exclusive range plus one: `x..(y+1)`
fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! { if_chain! {

View file

@ -1,183 +0,0 @@
use std::cmp;
use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_ast::attr;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
use rustc_target::abi::LayoutOf;
use rustc_target::spec::abi::Abi;
use rustc_target::spec::Target;
declare_clippy_lint! {
/// **What it does:** Checks for functions taking arguments by reference, where
/// the argument type is `Copy` and small enough to be more efficient to always
/// pass by value.
///
/// **Why is this bad?** In many calling conventions instances of structs will
/// be passed through registers if they fit into two or less general purpose
/// registers.
///
/// **Known problems:** This lint is target register size dependent, it is
/// limited to 32-bit to try and reduce portability problems between 32 and
/// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit
/// will be different.
///
/// The configuration option `trivial_copy_size_limit` can be set to override
/// this limit for a project.
///
/// This lint attempts to allow passing arguments by reference if a reference
/// to that argument is returned. This is implemented by comparing the lifetime
/// of the argument and return value for equality. However, this can cause
/// false positives in cases involving multiple lifetimes that are bounded by
/// each other.
///
/// **Example:**
///
/// ```rust
/// // Bad
/// fn foo(v: &u32) {}
/// ```
///
/// ```rust
/// // Better
/// fn foo(v: u32) {}
/// ```
pub TRIVIALLY_COPY_PASS_BY_REF,
pedantic,
"functions taking small copyable arguments by reference"
}
#[derive(Copy, Clone)]
pub struct TriviallyCopyPassByRef {
limit: u64,
}
impl<'tcx> TriviallyCopyPassByRef {
pub fn new(limit: Option<u64>, target: &Target) -> Self {
let limit = limit.unwrap_or_else(|| {
let bit_width = u64::from(target.pointer_width);
// Cap the calculated bit width at 32-bits to reduce
// portability problems between 32 and 64-bit targets
let bit_width = cmp::min(bit_width, 32);
#[allow(clippy::integer_division)]
let byte_width = bit_width / 8;
// Use a limit of 2 times the register byte width
byte_width * 2
});
Self { limit }
}
fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option<Span>) {
let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
let fn_sig = cx.tcx.fn_sig(fn_def_id);
let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
// Use lifetimes to determine if we're returning a reference to the
// argument. In that case we can't switch to pass-by-value as the
// argument will not live long enough.
let output_lts = match *fn_sig.output().kind() {
ty::Ref(output_lt, _, _) => vec![output_lt],
ty::Adt(_, substs) => substs.regions().collect(),
_ => vec![],
};
for (input, &ty) in decl.inputs.iter().zip(fn_sig.inputs()) {
// All spans generated from a proc-macro invocation are the same...
match span {
Some(s) if s == input.span => return,
_ => (),
}
if_chain! {
if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind();
if !output_lts.contains(&input_lt);
if is_copy(cx, ty);
if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
if size <= self.limit;
if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind;
then {
let value_type = if is_self_ty(decl_ty) {
"self".into()
} else {
snippet(cx, decl_ty.span, "_").into()
};
span_lint_and_sugg(
cx,
TRIVIALLY_COPY_PASS_BY_REF,
input.span,
&format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.limit),
"consider passing by value instead",
value_type,
Applicability::Unspecified,
);
}
}
}
}
}
impl_lint_pass!(TriviallyCopyPassByRef => [TRIVIALLY_COPY_PASS_BY_REF]);
impl<'tcx> LateLintPass<'tcx> for TriviallyCopyPassByRef {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
if item.span.from_expansion() {
return;
}
if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind {
self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None);
}
}
fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
kind: FnKind<'tcx>,
decl: &'tcx FnDecl<'_>,
_body: &'tcx Body<'_>,
span: Span,
hir_id: HirId,
) {
if span.from_expansion() {
return;
}
match kind {
FnKind::ItemFn(.., header, _, attrs) => {
if header.abi != Abi::Rust {
return;
}
for a in attrs {
if let Some(meta_items) = a.meta_item_list() {
if a.has_name(sym!(proc_macro_derive))
|| (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always)))
{
return;
}
}
}
},
FnKind::Method(..) => (),
FnKind::Closure(..) => return,
}
// Exclude non-inherent impls
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
ItemKind::Trait(..))
{
return;
}
}
self.check_poly_fn(cx, hir_id, decl, Some(span));
}
}

View file

@ -11,7 +11,7 @@ use rustc_hir as hir;
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{ use rustc_hir::{
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind,
TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
}; };
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -1224,7 +1224,8 @@ declare_clippy_lint! {
} }
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for casts to the same type. /// **What it does:** Checks for casts to the same type, casts of int literals to integer types
/// and casts of float literals to float types.
/// ///
/// **Why is this bad?** It's just unnecessary. /// **Why is this bad?** It's just unnecessary.
/// ///
@ -1233,6 +1234,14 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// let _ = 2i32 as i32; /// let _ = 2i32 as i32;
/// let _ = 0.5 as f32;
/// ```
///
/// Better:
///
/// ```rust
/// let _ = 2_i32;
/// let _ = 0.5_f32;
/// ``` /// ```
pub UNNECESSARY_CAST, pub UNNECESSARY_CAST,
complexity, complexity,
@ -1598,7 +1607,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
if let ExprKind::Cast(ref ex, _) = expr.kind { if let ExprKind::Cast(ref ex, _) = expr.kind {
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr));
lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to);
if let ExprKind::Lit(ref lit) = ex.kind { if let Some(lit) = get_numeric_literal(ex) {
let literal_str = snippet_opt(cx, ex.span).unwrap_or_default();
if_chain! { if_chain! {
if let LitKind::Int(n, _) = lit.node; if let LitKind::Int(n, _) = lit.node;
if let Some(src) = snippet_opt(cx, lit.span); if let Some(src) = snippet_opt(cx, lit.span);
@ -1608,19 +1619,19 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
let to_nbits = fp_ty_mantissa_nbits(cast_to); let to_nbits = fp_ty_mantissa_nbits(cast_to);
if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal();
then { then {
span_lint_and_sugg( let literal_str = if is_unary_neg(ex) { format!("-{}", num_lit.integer) } else { num_lit.integer.into() };
cx, show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
UNNECESSARY_CAST,
expr.span,
&format!("casting integer literal to `{}` is unnecessary", cast_to),
"try",
format!("{}_{}", n, cast_to),
Applicability::MachineApplicable,
);
return; return;
} }
} }
match lit.node { match lit.node {
LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => {
show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
},
LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => {
show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
},
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {},
_ => { _ => {
if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
@ -1646,6 +1657,37 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
} }
} }
fn is_unary_neg(expr: &Expr<'_>) -> bool {
matches!(expr.kind, ExprKind::Unary(UnOp::UnNeg, _))
}
fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> {
match expr.kind {
ExprKind::Lit(ref lit) => Some(lit),
ExprKind::Unary(UnOp::UnNeg, e) => {
if let ExprKind::Lit(ref lit) = e.kind {
Some(lit)
} else {
None
}
},
_ => None,
}
}
fn show_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) {
let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" };
span_lint_and_sugg(
cx,
UNNECESSARY_CAST,
expr.span,
&format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to),
"try",
format!("{}_{}", literal_str, cast_to),
Applicability::MachineApplicable,
);
}
fn lint_numeric_casts<'tcx>( fn lint_numeric_casts<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
expr: &Expr<'tcx>, expr: &Expr<'tcx>,

View file

@ -0,0 +1,50 @@
use crate::utils::{is_type_lang_item, match_function_call, paths, span_lint_and_help};
use rustc_hir::{lang_items, Expr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.
///
/// **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`.
///
/// **Known problems:** Does not catch cases if the user binds `std::mem::drop`
/// to a different name and calls it that way.
///
/// **Example:**
///
/// ```rust
/// struct S;
/// drop(std::mem::ManuallyDrop::new(S));
/// ```
/// Use instead:
/// ```rust
/// struct S;
/// unsafe {
/// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
/// }
/// ```
pub UNDROPPED_MANUALLY_DROPS,
correctness,
"use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
}
declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]);
impl LateLintPass<'tcx> for UndroppedManuallyDrops {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(ref args) = match_function_call(cx, expr, &paths::DROP) {
let ty = cx.typeck_results().expr_ty(&args[0]);
if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop) {
span_lint_and_help(
cx,
UNDROPPED_MANUALLY_DROPS,
expr.span,
"the inner value of this ManuallyDrop will not be dropped",
None,
"to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop",
);
}
}
}
}

View file

@ -150,6 +150,8 @@ define_Conf! {
(literal_representation_threshold, "literal_representation_threshold": u64, 16384), (literal_representation_threshold, "literal_representation_threshold": u64, 16384),
/// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
(trivial_copy_size_limit, "trivial_copy_size_limit": Option<u64>, None), (trivial_copy_size_limit, "trivial_copy_size_limit": Option<u64>, None),
/// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value.
(pass_by_value_size_limit, "pass_by_value_size_limit": u64, 256),
/// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
(too_many_lines_threshold, "too_many_lines_threshold": u64, 100), (too_many_lines_threshold, "too_many_lines_threshold": u64, 100),
/// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack

View file

@ -299,7 +299,7 @@ pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) -
hir::QPath::Resolved(_, path) => path.res, hir::QPath::Resolved(_, path) => path.res,
hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => { hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => {
if cx.tcx.has_typeck_results(id.owner.to_def_id()) { if cx.tcx.has_typeck_results(id.owner.to_def_id()) {
cx.tcx.typeck(id.owner.to_def_id().expect_local()).qpath_res(qpath, id) cx.tcx.typeck(id.owner).qpath_res(qpath, id)
} else { } else {
Res::Err Res::Err
} }

View file

@ -93,6 +93,8 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"]; pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];

View file

@ -19,12 +19,11 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) ->
used_mutably: FxHashSet::default(), used_mutably: FxHashSet::default(),
skip: false, skip: false,
}; };
let def_id = expr.hir_id.owner.to_def_id();
cx.tcx.infer_ctxt().enter(|infcx| { cx.tcx.infer_ctxt().enter(|infcx| {
ExprUseVisitor::new( ExprUseVisitor::new(
&mut delegate, &mut delegate,
&infcx, &infcx,
def_id.expect_local(), expr.hir_id.owner,
cx.param_env, cx.param_env,
cx.typeck_results(), cx.typeck_results(),
) )

View file

@ -62,10 +62,17 @@ vec![
}, },
Lint { Lint {
name: "await_holding_lock", name: "await_holding_lock",
group: "pedantic", group: "correctness",
desc: "Inside an async function, holding a MutexGuard while calling await", desc: "Inside an async function, holding a MutexGuard while calling await",
deprecation: None, deprecation: None,
module: "await_holding_lock", module: "await_holding_invalid",
},
Lint {
name: "await_holding_refcell_ref",
group: "correctness",
desc: "Inside an async function, holding a RefCell ref while calling await",
deprecation: None,
module: "await_holding_invalid",
}, },
Lint { Lint {
name: "bad_bit_mask", name: "bad_bit_mask",
@ -1061,6 +1068,13 @@ vec![
deprecation: None, deprecation: None,
module: "large_stack_arrays", module: "large_stack_arrays",
}, },
Lint {
name: "large_types_passed_by_value",
group: "pedantic",
desc: "functions taking large arguments by value",
deprecation: None,
module: "pass_by_ref_or_value",
},
Lint { Lint {
name: "len_without_is_empty", name: "len_without_is_empty",
group: "style", group: "style",
@ -1159,6 +1173,13 @@ vec![
deprecation: None, deprecation: None,
module: "manual_non_exhaustive", module: "manual_non_exhaustive",
}, },
Lint {
name: "manual_range_contains",
group: "style",
desc: "manually reimplementing {`Range`, `RangeInclusive`}`::contains`",
deprecation: None,
module: "ranges",
},
Lint { Lint {
name: "manual_saturating_arithmetic", name: "manual_saturating_arithmetic",
group: "style", group: "style",
@ -1183,7 +1204,7 @@ vec![
Lint { Lint {
name: "manual_unwrap_or", name: "manual_unwrap_or",
group: "complexity", group: "complexity",
desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`",
deprecation: None, deprecation: None,
module: "manual_unwrap_or", module: "manual_unwrap_or",
}, },
@ -1488,6 +1509,13 @@ vec![
deprecation: None, deprecation: None,
module: "mut_mut", module: "mut_mut",
}, },
Lint {
name: "mut_mutex_lock",
group: "style",
desc: "`&mut Mutex::lock` does unnecessary locking",
deprecation: None,
module: "mut_mutex_lock",
},
Lint { Lint {
name: "mut_range_bound", name: "mut_range_bound",
group: "complexity", group: "complexity",
@ -2125,6 +2153,13 @@ vec![
deprecation: None, deprecation: None,
module: "single_component_path_imports", module: "single_component_path_imports",
}, },
Lint {
name: "single_element_loop",
group: "complexity",
desc: "there is no reason to have a single element loop",
deprecation: None,
module: "loops",
},
Lint { Lint {
name: "single_match", name: "single_match",
group: "style", group: "style",
@ -2389,7 +2424,7 @@ vec![
group: "pedantic", group: "pedantic",
desc: "functions taking small copyable arguments by reference", desc: "functions taking small copyable arguments by reference",
deprecation: None, deprecation: None,
module: "trivially_copy_pass_by_ref", module: "pass_by_ref_or_value",
}, },
Lint { Lint {
name: "try_err", name: "try_err",
@ -2412,6 +2447,13 @@ vec![
deprecation: None, deprecation: None,
module: "trait_bounds", module: "trait_bounds",
}, },
Lint {
name: "undropped_manually_drops",
group: "correctness",
desc: "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value",
deprecation: None,
module: "undropped_manually_drops",
},
Lint { Lint {
name: "unicode_not_nfc", name: "unicode_not_nfc",
group: "pedantic", group: "pedantic",

View file

@ -1,4 +1,4 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1
error: aborting due to previous error error: aborting due to previous error

View file

@ -0,0 +1,86 @@
// edition:2018
#![warn(clippy::await_holding_refcell_ref)]
use std::cell::RefCell;
async fn bad(x: &RefCell<u32>) -> u32 {
let b = x.borrow();
baz().await
}
async fn bad_mut(x: &RefCell<u32>) -> u32 {
let b = x.borrow_mut();
baz().await
}
async fn good(x: &RefCell<u32>) -> u32 {
{
let b = x.borrow_mut();
let y = *b + 1;
}
baz().await;
let b = x.borrow_mut();
47
}
async fn baz() -> u32 {
42
}
async fn also_bad(x: &RefCell<u32>) -> u32 {
let first = baz().await;
let b = x.borrow_mut();
let second = baz().await;
let third = baz().await;
first + second + third
}
async fn less_bad(x: &RefCell<u32>) -> u32 {
let first = baz().await;
let b = x.borrow_mut();
let second = baz().await;
drop(b);
let third = baz().await;
first + second + third
}
async fn not_good(x: &RefCell<u32>) -> u32 {
let first = baz().await;
let second = {
let b = x.borrow_mut();
baz().await
};
let third = baz().await;
first + second + third
}
#[allow(clippy::manual_async_fn)]
fn block_bad(x: &RefCell<u32>) -> impl std::future::Future<Output = u32> + '_ {
async move {
let b = x.borrow_mut();
baz().await
}
}
fn main() {
let rc = RefCell::new(100);
good(&rc);
bad(&rc);
bad_mut(&rc);
also_bad(&rc);
less_bad(&rc);
not_good(&rc);
block_bad(&rc);
}

View file

@ -0,0 +1,95 @@
error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.
--> $DIR/await_holding_refcell_ref.rs:7:9
|
LL | let b = x.borrow();
| ^
|
= note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings`
note: these are all the await points this ref is held through
--> $DIR/await_holding_refcell_ref.rs:7:5
|
LL | / let b = x.borrow();
LL | | baz().await
LL | | }
| |_^
error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.
--> $DIR/await_holding_refcell_ref.rs:12:9
|
LL | let b = x.borrow_mut();
| ^
|
note: these are all the await points this ref is held through
--> $DIR/await_holding_refcell_ref.rs:12:5
|
LL | / let b = x.borrow_mut();
LL | | baz().await
LL | | }
| |_^
error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.
--> $DIR/await_holding_refcell_ref.rs:33:9
|
LL | let b = x.borrow_mut();
| ^
|
note: these are all the await points this ref is held through
--> $DIR/await_holding_refcell_ref.rs:33:5
|
LL | / let b = x.borrow_mut();
LL | |
LL | | let second = baz().await;
LL | |
... |
LL | | first + second + third
LL | | }
| |_^
error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.
--> $DIR/await_holding_refcell_ref.rs:45:9
|
LL | let b = x.borrow_mut();
| ^
|
note: these are all the await points this ref is held through
--> $DIR/await_holding_refcell_ref.rs:45:5
|
LL | / let b = x.borrow_mut();
LL | |
LL | | let second = baz().await;
LL | |
... |
LL | | first + second + third
LL | | }
| |_^
error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.
--> $DIR/await_holding_refcell_ref.rs:60:13
|
LL | let b = x.borrow_mut();
| ^
|
note: these are all the await points this ref is held through
--> $DIR/await_holding_refcell_ref.rs:60:9
|
LL | / let b = x.borrow_mut();
LL | | baz().await
LL | | };
| |_____^
error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.
--> $DIR/await_holding_refcell_ref.rs:72:13
|
LL | let b = x.borrow_mut();
| ^
|
note: these are all the await points this ref is held through
--> $DIR/await_holding_refcell_ref.rs:72:9
|
LL | / let b = x.borrow_mut();
LL | | baz().await
LL | | }
| |_____^
error: aborting due to 6 previous errors

View file

@ -1,6 +1,7 @@
// run-rustfix // run-rustfix
#[warn(clippy::bool_comparison)] #![warn(clippy::bool_comparison)]
fn main() { fn main() {
let x = true; let x = true;
if x { if x {
@ -127,3 +128,40 @@ fn issue4983() {
if b == a {}; if b == a {};
if !b == !a {}; if !b == !a {};
} }
macro_rules! m {
($func:ident) => {
$func()
};
}
fn func() -> bool {
true
}
#[allow(dead_code)]
fn issue3973() {
// ok, don't lint on `cfg` invocation
if false == cfg!(feature = "debugging") {}
if cfg!(feature = "debugging") == false {}
if true == cfg!(feature = "debugging") {}
if cfg!(feature = "debugging") == true {}
// lint, could be simplified
if !m!(func) {}
if !m!(func) {}
if m!(func) {}
if m!(func) {}
// no lint with a variable
let is_debug = false;
if is_debug == cfg!(feature = "debugging") {}
if cfg!(feature = "debugging") == is_debug {}
if is_debug == m!(func) {}
if m!(func) == is_debug {}
let is_debug = true;
if is_debug == cfg!(feature = "debugging") {}
if cfg!(feature = "debugging") == is_debug {}
if is_debug == m!(func) {}
if m!(func) == is_debug {}
}

View file

@ -1,6 +1,7 @@
// run-rustfix // run-rustfix
#[warn(clippy::bool_comparison)] #![warn(clippy::bool_comparison)]
fn main() { fn main() {
let x = true; let x = true;
if x == true { if x == true {
@ -127,3 +128,40 @@ fn issue4983() {
if b == a {}; if b == a {};
if !b == !a {}; if !b == !a {};
} }
macro_rules! m {
($func:ident) => {
$func()
};
}
fn func() -> bool {
true
}
#[allow(dead_code)]
fn issue3973() {
// ok, don't lint on `cfg` invocation
if false == cfg!(feature = "debugging") {}
if cfg!(feature = "debugging") == false {}
if true == cfg!(feature = "debugging") {}
if cfg!(feature = "debugging") == true {}
// lint, could be simplified
if false == m!(func) {}
if m!(func) == false {}
if true == m!(func) {}
if m!(func) == true {}
// no lint with a variable
let is_debug = false;
if is_debug == cfg!(feature = "debugging") {}
if cfg!(feature = "debugging") == is_debug {}
if is_debug == m!(func) {}
if m!(func) == is_debug {}
let is_debug = true;
if is_debug == cfg!(feature = "debugging") {}
if cfg!(feature = "debugging") == is_debug {}
if is_debug == m!(func) {}
if m!(func) == is_debug {}
}

View file

@ -1,5 +1,5 @@
error: equality checks against true are unnecessary error: equality checks against true are unnecessary
--> $DIR/bool_comparison.rs:6:8 --> $DIR/bool_comparison.rs:7:8
| |
LL | if x == true { LL | if x == true {
| ^^^^^^^^^ help: try simplifying it as shown: `x` | ^^^^^^^^^ help: try simplifying it as shown: `x`
@ -7,106 +7,130 @@ LL | if x == true {
= note: `-D clippy::bool-comparison` implied by `-D warnings` = note: `-D clippy::bool-comparison` implied by `-D warnings`
error: equality checks against false can be replaced by a negation error: equality checks against false can be replaced by a negation
--> $DIR/bool_comparison.rs:11:8 --> $DIR/bool_comparison.rs:12:8
| |
LL | if x == false { LL | if x == false {
| ^^^^^^^^^^ help: try simplifying it as shown: `!x` | ^^^^^^^^^^ help: try simplifying it as shown: `!x`
error: equality checks against true are unnecessary error: equality checks against true are unnecessary
--> $DIR/bool_comparison.rs:16:8 --> $DIR/bool_comparison.rs:17:8
| |
LL | if true == x { LL | if true == x {
| ^^^^^^^^^ help: try simplifying it as shown: `x` | ^^^^^^^^^ help: try simplifying it as shown: `x`
error: equality checks against false can be replaced by a negation error: equality checks against false can be replaced by a negation
--> $DIR/bool_comparison.rs:21:8 --> $DIR/bool_comparison.rs:22:8
| |
LL | if false == x { LL | if false == x {
| ^^^^^^^^^^ help: try simplifying it as shown: `!x` | ^^^^^^^^^^ help: try simplifying it as shown: `!x`
error: inequality checks against true can be replaced by a negation error: inequality checks against true can be replaced by a negation
--> $DIR/bool_comparison.rs:26:8 --> $DIR/bool_comparison.rs:27:8
| |
LL | if x != true { LL | if x != true {
| ^^^^^^^^^ help: try simplifying it as shown: `!x` | ^^^^^^^^^ help: try simplifying it as shown: `!x`
error: inequality checks against false are unnecessary error: inequality checks against false are unnecessary
--> $DIR/bool_comparison.rs:31:8 --> $DIR/bool_comparison.rs:32:8
| |
LL | if x != false { LL | if x != false {
| ^^^^^^^^^^ help: try simplifying it as shown: `x` | ^^^^^^^^^^ help: try simplifying it as shown: `x`
error: inequality checks against true can be replaced by a negation error: inequality checks against true can be replaced by a negation
--> $DIR/bool_comparison.rs:36:8 --> $DIR/bool_comparison.rs:37:8
| |
LL | if true != x { LL | if true != x {
| ^^^^^^^^^ help: try simplifying it as shown: `!x` | ^^^^^^^^^ help: try simplifying it as shown: `!x`
error: inequality checks against false are unnecessary error: inequality checks against false are unnecessary
--> $DIR/bool_comparison.rs:41:8 --> $DIR/bool_comparison.rs:42:8
| |
LL | if false != x { LL | if false != x {
| ^^^^^^^^^^ help: try simplifying it as shown: `x` | ^^^^^^^^^^ help: try simplifying it as shown: `x`
error: less than comparison against true can be replaced by a negation error: less than comparison against true can be replaced by a negation
--> $DIR/bool_comparison.rs:46:8 --> $DIR/bool_comparison.rs:47:8
| |
LL | if x < true { LL | if x < true {
| ^^^^^^^^ help: try simplifying it as shown: `!x` | ^^^^^^^^ help: try simplifying it as shown: `!x`
error: greater than checks against false are unnecessary error: greater than checks against false are unnecessary
--> $DIR/bool_comparison.rs:51:8 --> $DIR/bool_comparison.rs:52:8
| |
LL | if false < x { LL | if false < x {
| ^^^^^^^^^ help: try simplifying it as shown: `x` | ^^^^^^^^^ help: try simplifying it as shown: `x`
error: greater than checks against false are unnecessary error: greater than checks against false are unnecessary
--> $DIR/bool_comparison.rs:56:8 --> $DIR/bool_comparison.rs:57:8
| |
LL | if x > false { LL | if x > false {
| ^^^^^^^^^ help: try simplifying it as shown: `x` | ^^^^^^^^^ help: try simplifying it as shown: `x`
error: less than comparison against true can be replaced by a negation error: less than comparison against true can be replaced by a negation
--> $DIR/bool_comparison.rs:61:8 --> $DIR/bool_comparison.rs:62:8
| |
LL | if true > x { LL | if true > x {
| ^^^^^^^^ help: try simplifying it as shown: `!x` | ^^^^^^^^ help: try simplifying it as shown: `!x`
error: order comparisons between booleans can be simplified error: order comparisons between booleans can be simplified
--> $DIR/bool_comparison.rs:67:8 --> $DIR/bool_comparison.rs:68:8
| |
LL | if x < y { LL | if x < y {
| ^^^^^ help: try simplifying it as shown: `!x & y` | ^^^^^ help: try simplifying it as shown: `!x & y`
error: order comparisons between booleans can be simplified error: order comparisons between booleans can be simplified
--> $DIR/bool_comparison.rs:72:8 --> $DIR/bool_comparison.rs:73:8
| |
LL | if x > y { LL | if x > y {
| ^^^^^ help: try simplifying it as shown: `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 --> $DIR/bool_comparison.rs:121:8
| |
LL | if a == !b {}; LL | if a == !b {};
| ^^^^^^^ help: try simplifying it as shown: `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 --> $DIR/bool_comparison.rs:122:8
| |
LL | if !a == b {}; LL | if !a == b {};
| ^^^^^^^ help: try simplifying it as shown: `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 --> $DIR/bool_comparison.rs:126:8
| |
LL | if b == !a {}; LL | if b == !a {};
| ^^^^^^^ help: try simplifying it as shown: `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 --> $DIR/bool_comparison.rs:127:8
| |
LL | if !b == a {}; LL | if !b == a {};
| ^^^^^^^ help: try simplifying it as shown: `b != a` | ^^^^^^^ help: try simplifying it as shown: `b != a`
error: aborting due to 18 previous errors error: equality checks against false can be replaced by a negation
--> $DIR/bool_comparison.rs:151:8
|
LL | if false == m!(func) {}
| ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)`
error: equality checks against false can be replaced by a negation
--> $DIR/bool_comparison.rs:152:8
|
LL | if m!(func) == false {}
| ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)`
error: equality checks against true are unnecessary
--> $DIR/bool_comparison.rs:153:8
|
LL | if true == m!(func) {}
| ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)`
error: equality checks against true are unnecessary
--> $DIR/bool_comparison.rs:154:8
|
LL | if m!(func) == true {}
| ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)`
error: aborting due to 22 previous errors

View file

@ -124,7 +124,7 @@ error: casting integer literal to `f64` is unnecessary
--> $DIR/cast_size_32bit.rs:34:5 --> $DIR/cast_size_32bit.rs:34:5
| |
LL | 3_999_999_999usize as f64; LL | 3_999_999_999usize as f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3999999999_f64` | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3_999_999_999_f64`
| |
= note: `-D clippy::unnecessary-cast` implied by `-D warnings` = note: `-D clippy::unnecessary-cast` implied by `-D warnings`

View file

@ -12,13 +12,14 @@ LL | | }
| |
= note: `-D clippy::while-let-loop` implied by `-D warnings` = note: `-D clippy::while-let-loop` implied by `-D warnings`
error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. error: empty `loop {}` wastes CPU cycles
--> $DIR/ice-360.rs:10:9 --> $DIR/ice-360.rs:10:9
| |
LL | loop {} LL | loop {}
| ^^^^^^^ | ^^^^^^^
| |
= note: `-D clippy::empty-loop` implied by `-D warnings` = note: `-D clippy::empty-loop` implied by `-D warnings`
= help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View file

@ -1,22 +1,27 @@
error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. error: empty `loop {}` wastes CPU cycles
--> $DIR/empty_loop.rs:9:5 --> $DIR/empty_loop.rs:9:5
| |
LL | loop {} LL | loop {}
| ^^^^^^^ | ^^^^^^^
| |
= note: `-D clippy::empty-loop` implied by `-D warnings` = note: `-D clippy::empty-loop` implied by `-D warnings`
= help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. error: empty `loop {}` wastes CPU cycles
--> $DIR/empty_loop.rs:11:9 --> $DIR/empty_loop.rs:11:9
| |
LL | loop {} LL | loop {}
| ^^^^^^^ | ^^^^^^^
|
= help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. error: empty `loop {}` wastes CPU cycles
--> $DIR/empty_loop.rs:15:9 --> $DIR/empty_loop.rs:15:9
| |
LL | 'inner: loop {} LL | 'inner: loop {}
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
|
= help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
error: aborting due to 3 previous errors error: aborting due to 3 previous errors

View file

@ -6,6 +6,7 @@
#[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)] #[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
#[allow(clippy::nonminimal_bool)] #[allow(clippy::nonminimal_bool)]
#[allow(unused)] #[allow(unused)]
#[allow(clippy::unnecessary_cast)]
fn main() { fn main() {
// simple values and comparisons // simple values and comparisons
1 == 1; 1 == 1;

View file

@ -1,5 +1,5 @@
error: equal expressions as operands to `==` error: equal expressions as operands to `==`
--> $DIR/eq_op.rs:11:5 --> $DIR/eq_op.rs:12:5
| |
LL | 1 == 1; LL | 1 == 1;
| ^^^^^^ | ^^^^^^
@ -7,157 +7,157 @@ LL | 1 == 1;
= note: `-D clippy::eq-op` implied by `-D warnings` = note: `-D clippy::eq-op` implied by `-D warnings`
error: equal expressions as operands to `==` error: equal expressions as operands to `==`
--> $DIR/eq_op.rs:12:5 --> $DIR/eq_op.rs:13:5
| |
LL | "no" == "no"; LL | "no" == "no";
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: equal expressions as operands to `!=` error: equal expressions as operands to `!=`
--> $DIR/eq_op.rs:14:5 --> $DIR/eq_op.rs:15:5
| |
LL | false != false; LL | false != false;
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: equal expressions as operands to `<` error: equal expressions as operands to `<`
--> $DIR/eq_op.rs:15:5 --> $DIR/eq_op.rs:16:5
| |
LL | 1.5 < 1.5; LL | 1.5 < 1.5;
| ^^^^^^^^^ | ^^^^^^^^^
error: equal expressions as operands to `>=` error: equal expressions as operands to `>=`
--> $DIR/eq_op.rs:16:5 --> $DIR/eq_op.rs:17:5
| |
LL | 1u64 >= 1u64; LL | 1u64 >= 1u64;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: equal expressions as operands to `&` error: equal expressions as operands to `&`
--> $DIR/eq_op.rs:19:5 --> $DIR/eq_op.rs:20:5
| |
LL | (1 as u64) & (1 as u64); LL | (1 as u64) & (1 as u64);
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `^` error: equal expressions as operands to `^`
--> $DIR/eq_op.rs:20:5 --> $DIR/eq_op.rs:21:5
| |
LL | 1 ^ ((((((1)))))); LL | 1 ^ ((((((1))))));
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `<` error: equal expressions as operands to `<`
--> $DIR/eq_op.rs:23:5 --> $DIR/eq_op.rs:24:5
| |
LL | (-(2) < -(2)); LL | (-(2) < -(2));
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: equal expressions as operands to `==` error: equal expressions as operands to `==`
--> $DIR/eq_op.rs:24:5 --> $DIR/eq_op.rs:25:5
| |
LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `&` error: equal expressions as operands to `&`
--> $DIR/eq_op.rs:24:6 --> $DIR/eq_op.rs:25:6
| |
LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `&` error: equal expressions as operands to `&`
--> $DIR/eq_op.rs:24:27 --> $DIR/eq_op.rs:25:27
| |
LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `==` error: equal expressions as operands to `==`
--> $DIR/eq_op.rs:25:5 --> $DIR/eq_op.rs:26:5
| |
LL | (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4; LL | (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `!=` error: equal expressions as operands to `!=`
--> $DIR/eq_op.rs:28:5 --> $DIR/eq_op.rs:29:5
| |
LL | ([1] != [1]); LL | ([1] != [1]);
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: equal expressions as operands to `!=` error: equal expressions as operands to `!=`
--> $DIR/eq_op.rs:29:5 --> $DIR/eq_op.rs:30:5
| |
LL | ((1, 2) != (1, 2)); LL | ((1, 2) != (1, 2));
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `==` error: equal expressions as operands to `==`
--> $DIR/eq_op.rs:33:5 --> $DIR/eq_op.rs:34:5
| |
LL | 1 + 1 == 2; LL | 1 + 1 == 2;
| ^^^^^^^^^^ | ^^^^^^^^^^
error: equal expressions as operands to `==` error: equal expressions as operands to `==`
--> $DIR/eq_op.rs:34:5 --> $DIR/eq_op.rs:35:5
| |
LL | 1 - 1 == 0; LL | 1 - 1 == 0;
| ^^^^^^^^^^ | ^^^^^^^^^^
error: equal expressions as operands to `-` error: equal expressions as operands to `-`
--> $DIR/eq_op.rs:34:5 --> $DIR/eq_op.rs:35:5
| |
LL | 1 - 1 == 0; LL | 1 - 1 == 0;
| ^^^^^ | ^^^^^
error: equal expressions as operands to `-` error: equal expressions as operands to `-`
--> $DIR/eq_op.rs:36:5 --> $DIR/eq_op.rs:37:5
| |
LL | 1 - 1; LL | 1 - 1;
| ^^^^^ | ^^^^^
error: equal expressions as operands to `/` error: equal expressions as operands to `/`
--> $DIR/eq_op.rs:37:5 --> $DIR/eq_op.rs:38:5
| |
LL | 1 / 1; LL | 1 / 1;
| ^^^^^ | ^^^^^
error: equal expressions as operands to `&&` error: equal expressions as operands to `&&`
--> $DIR/eq_op.rs:38:5 --> $DIR/eq_op.rs:39:5
| |
LL | true && true; LL | true && true;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: equal expressions as operands to `||` error: equal expressions as operands to `||`
--> $DIR/eq_op.rs:40:5 --> $DIR/eq_op.rs:41:5
| |
LL | true || true; LL | true || true;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: equal expressions as operands to `&&` error: equal expressions as operands to `&&`
--> $DIR/eq_op.rs:46:5 --> $DIR/eq_op.rs:47:5
| |
LL | a == b && b == a; LL | a == b && b == a;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: equal expressions as operands to `&&` error: equal expressions as operands to `&&`
--> $DIR/eq_op.rs:47:5 --> $DIR/eq_op.rs:48:5
| |
LL | a != b && b != a; LL | a != b && b != a;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: equal expressions as operands to `&&` error: equal expressions as operands to `&&`
--> $DIR/eq_op.rs:48:5 --> $DIR/eq_op.rs:49:5
| |
LL | a < b && b > a; LL | a < b && b > a;
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: equal expressions as operands to `&&` error: equal expressions as operands to `&&`
--> $DIR/eq_op.rs:49:5 --> $DIR/eq_op.rs:50:5
| |
LL | a <= b && b >= a; LL | a <= b && b >= a;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: equal expressions as operands to `==` error: equal expressions as operands to `==`
--> $DIR/eq_op.rs:52:5 --> $DIR/eq_op.rs:53:5
| |
LL | a == a; LL | a == a;
| ^^^^^^ | ^^^^^^
error: equal expressions as operands to `/` error: equal expressions as operands to `/`
--> $DIR/eq_op.rs:62:20 --> $DIR/eq_op.rs:63:20
| |
LL | const D: u32 = A / A; LL | const D: u32 = A / A;
| ^^^^^ | ^^^^^

View file

@ -3,7 +3,8 @@
clippy::blacklisted_name, clippy::blacklisted_name,
clippy::collapsible_if, clippy::collapsible_if,
clippy::ifs_same_cond, clippy::ifs_same_cond,
clippy::needless_return clippy::needless_return,
clippy::single_element_loop
)] )]
fn if_same_then_else2() -> Result<&'static str, ()> { fn if_same_then_else2() -> Result<&'static str, ()> {

View file

@ -1,5 +1,5 @@
error: this `if` has identical blocks error: this `if` has identical blocks
--> $DIR/if_same_then_else2.rs:19:12 --> $DIR/if_same_then_else2.rs:20:12
| |
LL | } else { LL | } else {
| ____________^ | ____________^
@ -13,7 +13,7 @@ LL | | }
| |
= note: `-D clippy::if-same-then-else` implied by `-D warnings` = note: `-D clippy::if-same-then-else` implied by `-D warnings`
note: same as this note: same as this
--> $DIR/if_same_then_else2.rs:10:13 --> $DIR/if_same_then_else2.rs:11:13
| |
LL | if true { LL | if true {
| _____________^ | _____________^
@ -26,7 +26,7 @@ LL | | } else {
| |_____^ | |_____^
error: this `if` has identical blocks error: this `if` has identical blocks
--> $DIR/if_same_then_else2.rs:33:12 --> $DIR/if_same_then_else2.rs:34:12
| |
LL | } else { LL | } else {
| ____________^ | ____________^
@ -36,7 +36,7 @@ LL | | }
| |_____^ | |_____^
| |
note: same as this note: same as this
--> $DIR/if_same_then_else2.rs:31:13 --> $DIR/if_same_then_else2.rs:32:13
| |
LL | if true { LL | if true {
| _____________^ | _____________^
@ -45,7 +45,7 @@ LL | | } else {
| |_____^ | |_____^
error: this `if` has identical blocks error: this `if` has identical blocks
--> $DIR/if_same_then_else2.rs:40:12 --> $DIR/if_same_then_else2.rs:41:12
| |
LL | } else { LL | } else {
| ____________^ | ____________^
@ -55,7 +55,7 @@ LL | | }
| |_____^ | |_____^
| |
note: same as this note: same as this
--> $DIR/if_same_then_else2.rs:38:13 --> $DIR/if_same_then_else2.rs:39:13
| |
LL | if true { LL | if true {
| _____________^ | _____________^
@ -64,7 +64,7 @@ LL | | } else {
| |_____^ | |_____^
error: this `if` has identical blocks error: this `if` has identical blocks
--> $DIR/if_same_then_else2.rs:90:12 --> $DIR/if_same_then_else2.rs:91:12
| |
LL | } else { LL | } else {
| ____________^ | ____________^
@ -74,7 +74,7 @@ LL | | };
| |_____^ | |_____^
| |
note: same as this note: same as this
--> $DIR/if_same_then_else2.rs:88:21 --> $DIR/if_same_then_else2.rs:89:21
| |
LL | let _ = if true { LL | let _ = if true {
| _____________________^ | _____________________^
@ -83,7 +83,7 @@ LL | | } else {
| |_____^ | |_____^
error: this `if` has identical blocks error: this `if` has identical blocks
--> $DIR/if_same_then_else2.rs:97:12 --> $DIR/if_same_then_else2.rs:98:12
| |
LL | } else { LL | } else {
| ____________^ | ____________^
@ -93,7 +93,7 @@ LL | | }
| |_____^ | |_____^
| |
note: same as this note: same as this
--> $DIR/if_same_then_else2.rs:95:13 --> $DIR/if_same_then_else2.rs:96:13
| |
LL | if true { LL | if true {
| _____________^ | _____________^
@ -102,7 +102,7 @@ LL | | } else {
| |_____^ | |_____^
error: this `if` has identical blocks error: this `if` has identical blocks
--> $DIR/if_same_then_else2.rs:122:12 --> $DIR/if_same_then_else2.rs:123:12
| |
LL | } else { LL | } else {
| ____________^ | ____________^
@ -112,7 +112,7 @@ LL | | }
| |_____^ | |_____^
| |
note: same as this note: same as this
--> $DIR/if_same_then_else2.rs:119:20 --> $DIR/if_same_then_else2.rs:120:20
| |
LL | } else if true { LL | } else if true {
| ____________________^ | ____________________^

View file

@ -0,0 +1,66 @@
// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)"
// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
#![warn(clippy::large_types_passed_by_value)]
pub struct Large([u8; 2048]);
#[derive(Clone, Copy)]
pub struct LargeAndCopy([u8; 2048]);
pub struct Small([u8; 4]);
#[derive(Clone, Copy)]
pub struct SmallAndCopy([u8; 4]);
fn small(a: Small, b: SmallAndCopy) {}
fn not_copy(a: Large) {}
fn by_ref(a: &Large, b: &LargeAndCopy) {}
fn mutable(mut a: LargeAndCopy) {}
fn bad(a: LargeAndCopy) {}
pub fn bad_but_pub(a: LargeAndCopy) {}
impl LargeAndCopy {
fn self_is_ok(self) {}
fn other_is_not_ok(self, other: LargeAndCopy) {}
fn unless_other_can_change(self, mut other: LargeAndCopy) {}
pub fn or_were_in_public(self, other: LargeAndCopy) {}
}
trait LargeTypeDevourer {
fn devoure_array(&self, array: [u8; 6666]);
fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy));
fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy));
}
pub trait PubLargeTypeDevourer {
fn devoure_array_in_public(&self, array: [u8; 6666]);
}
struct S {}
impl LargeTypeDevourer for S {
fn devoure_array(&self, array: [u8; 6666]) {
todo!();
}
fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy)) {
todo!();
}
fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)) {
todo!();
}
}
#[inline(always)]
fn foo_always(x: LargeAndCopy) {
todo!();
}
#[inline(never)]
fn foo_never(x: LargeAndCopy) {
todo!();
}
#[inline]
fn foo(x: LargeAndCopy) {
todo!();
}
fn main() {}

View file

@ -0,0 +1,52 @@
error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
--> $DIR/large_types_passed_by_value.rs:20:11
|
LL | fn bad(a: LargeAndCopy) {}
| ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy`
|
= note: `-D clippy::large-types-passed-by-value` implied by `-D warnings`
error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
--> $DIR/large_types_passed_by_value.rs:25:37
|
LL | fn other_is_not_ok(self, other: LargeAndCopy) {}
| ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy`
error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
--> $DIR/large_types_passed_by_value.rs:31:36
|
LL | fn devoure_array(&self, array: [u8; 6666]);
| ^^^^^^^^^^ help: consider passing by reference instead: `&[u8; 6666]`
error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
--> $DIR/large_types_passed_by_value.rs:32:34
|
LL | fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider passing by reference instead: `&(LargeAndCopy, LargeAndCopy)`
error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
--> $DIR/large_types_passed_by_value.rs:33:50
|
LL | fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy));
| ^^^^^^^^^^ help: consider passing by reference instead: `&[u8; 6666]`
error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
--> $DIR/large_types_passed_by_value.rs:33:67
|
LL | fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider passing by reference instead: `&(LargeAndCopy, LargeAndCopy)`
error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
--> $DIR/large_types_passed_by_value.rs:58:17
|
LL | fn foo_never(x: LargeAndCopy) {
| ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy`
error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
--> $DIR/large_types_passed_by_value.rs:62:11
|
LL | fn foo(x: LargeAndCopy) {
| ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy`
error: aborting due to 8 previous errors

View file

@ -1,7 +1,8 @@
// run-rustfix // run-rustfix
#![allow(dead_code)] #![allow(dead_code)]
#![allow(unused_variables)]
fn unwrap_or() { fn option_unwrap_or() {
// int case // int case
Some(1).unwrap_or(42); Some(1).unwrap_or(42);
@ -65,4 +66,74 @@ fn unwrap_or() {
}; };
} }
fn result_unwrap_or() {
// int case
Ok::<i32, &str>(1).unwrap_or(42);
// int case, scrutinee is a binding
let a = Ok::<i32, &str>(1);
a.unwrap_or(42);
// int case, suggestion must surround Result expr with parenthesis
(Ok(1) as Result<i32, &str>).unwrap_or(42);
// method call case, suggestion must not surround Result expr `s.method()` with parenthesis
struct S {}
impl S {
fn method(self) -> Option<i32> {
Some(42)
}
}
let s = S {};
s.method().unwrap_or(42);
// int case reversed
Ok::<i32, &str>(1).unwrap_or(42);
// richer none expr
Ok::<i32, &str>(1).unwrap_or(1 + 42);
// multiline case
#[rustfmt::skip]
Ok::<i32, &str>(1).unwrap_or({
42 + 42
+ 42 + 42 + 42
+ 42 + 42 + 42
});
// string case
Ok::<&str, &str>("Bob").unwrap_or("Alice");
// don't lint
match Ok::<i32, &str>(1) {
Ok(i) => i + 2,
Err(_) => 42,
};
match Ok::<i32, &str>(1) {
Ok(i) => i,
Err(_) => return,
};
for j in 0..4 {
match Ok::<i32, &str>(j) {
Ok(i) => i,
Err(_) => continue,
};
match Ok::<i32, &str>(j) {
Ok(i) => i,
Err(_) => break,
};
}
// don't lint, Err value is used
match Ok::<&str, &str>("Alice") {
Ok(s) => s,
Err(s) => s,
};
// could lint, but unused_variables takes care of it
match Ok::<&str, &str>("Alice") {
Ok(s) => s,
Err(s) => "Bob",
};
}
fn main() {} fn main() {}

View file

@ -1,7 +1,8 @@
// run-rustfix // run-rustfix
#![allow(dead_code)] #![allow(dead_code)]
#![allow(unused_variables)]
fn unwrap_or() { fn option_unwrap_or() {
// int case // int case
match Some(1) { match Some(1) {
Some(i) => i, Some(i) => i,
@ -80,4 +81,98 @@ fn unwrap_or() {
}; };
} }
fn result_unwrap_or() {
// int case
match Ok::<i32, &str>(1) {
Ok(i) => i,
Err(_) => 42,
};
// int case, scrutinee is a binding
let a = Ok::<i32, &str>(1);
match a {
Ok(i) => i,
Err(_) => 42,
};
// int case, suggestion must surround Result expr with parenthesis
match Ok(1) as Result<i32, &str> {
Ok(i) => i,
Err(_) => 42,
};
// method call case, suggestion must not surround Result expr `s.method()` with parenthesis
struct S {}
impl S {
fn method(self) -> Option<i32> {
Some(42)
}
}
let s = S {};
match s.method() {
Some(i) => i,
None => 42,
};
// int case reversed
match Ok::<i32, &str>(1) {
Err(_) => 42,
Ok(i) => i,
};
// richer none expr
match Ok::<i32, &str>(1) {
Ok(i) => i,
Err(_) => 1 + 42,
};
// multiline case
#[rustfmt::skip]
match Ok::<i32, &str>(1) {
Ok(i) => i,
Err(_) => {
42 + 42
+ 42 + 42 + 42
+ 42 + 42 + 42
}
};
// string case
match Ok::<&str, &str>("Bob") {
Ok(i) => i,
Err(_) => "Alice",
};
// don't lint
match Ok::<i32, &str>(1) {
Ok(i) => i + 2,
Err(_) => 42,
};
match Ok::<i32, &str>(1) {
Ok(i) => i,
Err(_) => return,
};
for j in 0..4 {
match Ok::<i32, &str>(j) {
Ok(i) => i,
Err(_) => continue,
};
match Ok::<i32, &str>(j) {
Ok(i) => i,
Err(_) => break,
};
}
// don't lint, Err value is used
match Ok::<&str, &str>("Alice") {
Ok(s) => s,
Err(s) => s,
};
// could lint, but unused_variables takes care of it
match Ok::<&str, &str>("Alice") {
Ok(s) => s,
Err(s) => "Bob",
};
}
fn main() {} fn main() {}

View file

@ -1,5 +1,5 @@
error: this pattern reimplements `Option::unwrap_or` error: this pattern reimplements `Option::unwrap_or`
--> $DIR/manual_unwrap_or.rs:6:5 --> $DIR/manual_unwrap_or.rs:7:5
| |
LL | / match Some(1) { LL | / match Some(1) {
LL | | Some(i) => i, LL | | Some(i) => i,
@ -10,7 +10,7 @@ LL | | };
= note: `-D clippy::manual-unwrap-or` implied by `-D warnings` = note: `-D clippy::manual-unwrap-or` implied by `-D warnings`
error: this pattern reimplements `Option::unwrap_or` error: this pattern reimplements `Option::unwrap_or`
--> $DIR/manual_unwrap_or.rs:12:5 --> $DIR/manual_unwrap_or.rs:13:5
| |
LL | / match Some(1) { LL | / match Some(1) {
LL | | None => 42, LL | | None => 42,
@ -19,7 +19,7 @@ LL | | };
| |_____^ help: replace with: `Some(1).unwrap_or(42)` | |_____^ help: replace with: `Some(1).unwrap_or(42)`
error: this pattern reimplements `Option::unwrap_or` error: this pattern reimplements `Option::unwrap_or`
--> $DIR/manual_unwrap_or.rs:18:5 --> $DIR/manual_unwrap_or.rs:19:5
| |
LL | / match Some(1) { LL | / match Some(1) {
LL | | Some(i) => i, LL | | Some(i) => i,
@ -28,7 +28,7 @@ LL | | };
| |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)`
error: this pattern reimplements `Option::unwrap_or` error: this pattern reimplements `Option::unwrap_or`
--> $DIR/manual_unwrap_or.rs:25:5 --> $DIR/manual_unwrap_or.rs:26:5
| |
LL | / match Some(1) { LL | / match Some(1) {
LL | | Some(i) => i, LL | | Some(i) => i,
@ -49,7 +49,7 @@ LL | });
| |
error: this pattern reimplements `Option::unwrap_or` error: this pattern reimplements `Option::unwrap_or`
--> $DIR/manual_unwrap_or.rs:35:5 --> $DIR/manual_unwrap_or.rs:36:5
| |
LL | / match Some("Bob") { LL | / match Some("Bob") {
LL | | Some(i) => i, LL | | Some(i) => i,
@ -57,5 +57,89 @@ LL | | None => "Alice",
LL | | }; LL | | };
| |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")`
error: aborting due to 5 previous errors error: this pattern reimplements `Result::unwrap_or`
--> $DIR/manual_unwrap_or.rs:86:5
|
LL | / match Ok::<i32, &str>(1) {
LL | | Ok(i) => i,
LL | | Err(_) => 42,
LL | | };
| |_____^ help: replace with: `Ok::<i32, &str>(1).unwrap_or(42)`
error: this pattern reimplements `Result::unwrap_or`
--> $DIR/manual_unwrap_or.rs:93:5
|
LL | / match a {
LL | | Ok(i) => i,
LL | | Err(_) => 42,
LL | | };
| |_____^ help: replace with: `a.unwrap_or(42)`
error: this pattern reimplements `Result::unwrap_or`
--> $DIR/manual_unwrap_or.rs:99:5
|
LL | / match Ok(1) as Result<i32, &str> {
LL | | Ok(i) => i,
LL | | Err(_) => 42,
LL | | };
| |_____^ help: replace with: `(Ok(1) as Result<i32, &str>).unwrap_or(42)`
error: this pattern reimplements `Option::unwrap_or`
--> $DIR/manual_unwrap_or.rs:112:5
|
LL | / match s.method() {
LL | | Some(i) => i,
LL | | None => 42,
LL | | };
| |_____^ help: replace with: `s.method().unwrap_or(42)`
error: this pattern reimplements `Result::unwrap_or`
--> $DIR/manual_unwrap_or.rs:118:5
|
LL | / match Ok::<i32, &str>(1) {
LL | | Err(_) => 42,
LL | | Ok(i) => i,
LL | | };
| |_____^ help: replace with: `Ok::<i32, &str>(1).unwrap_or(42)`
error: this pattern reimplements `Result::unwrap_or`
--> $DIR/manual_unwrap_or.rs:124:5
|
LL | / match Ok::<i32, &str>(1) {
LL | | Ok(i) => i,
LL | | Err(_) => 1 + 42,
LL | | };
| |_____^ help: replace with: `Ok::<i32, &str>(1).unwrap_or(1 + 42)`
error: this pattern reimplements `Result::unwrap_or`
--> $DIR/manual_unwrap_or.rs:131:5
|
LL | / match Ok::<i32, &str>(1) {
LL | | Ok(i) => i,
LL | | Err(_) => {
LL | | 42 + 42
... |
LL | | }
LL | | };
| |_____^
|
help: replace with
|
LL | Ok::<i32, &str>(1).unwrap_or({
LL | 42 + 42
LL | + 42 + 42 + 42
LL | + 42 + 42 + 42
LL | });
|
error: this pattern reimplements `Result::unwrap_or`
--> $DIR/manual_unwrap_or.rs:141:5
|
LL | / match Ok::<&str, &str>("Bob") {
LL | | Ok(i) => i,
LL | | Err(_) => "Alice",
LL | | };
| |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")`
error: aborting due to 13 previous errors

View file

@ -0,0 +1,21 @@
// run-rustfix
#![allow(dead_code, unused_mut)]
#![warn(clippy::mut_mutex_lock)]
use std::sync::{Arc, Mutex};
fn mut_mutex_lock() {
let mut value_rc = Arc::new(Mutex::new(42_u8));
let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
let mut value = value_mutex.get_mut().unwrap();
*value += 1;
}
fn no_owned_mutex_lock() {
let mut value_rc = Arc::new(Mutex::new(42_u8));
let mut value = value_rc.lock().unwrap();
*value += 1;
}
fn main() {}

View file

@ -0,0 +1,21 @@
// run-rustfix
#![allow(dead_code, unused_mut)]
#![warn(clippy::mut_mutex_lock)]
use std::sync::{Arc, Mutex};
fn mut_mutex_lock() {
let mut value_rc = Arc::new(Mutex::new(42_u8));
let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
let mut value = value_mutex.lock().unwrap();
*value += 1;
}
fn no_owned_mutex_lock() {
let mut value_rc = Arc::new(Mutex::new(42_u8));
let mut value = value_rc.lock().unwrap();
*value += 1;
}
fn main() {}

View file

@ -0,0 +1,10 @@
error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference
--> $DIR/mut_mutex_lock.rs:11:33
|
LL | let mut value = value_mutex.lock().unwrap();
| ^^^^ help: change this to: `get_mut`
|
= note: `-D clippy::mut-mutex-lock` implied by `-D warnings`
error: aborting due to previous error

View file

@ -357,4 +357,15 @@ mod nested_elision_sites {
} }
} }
mod issue6159 {
use std::ops::Deref;
pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R
where
T: Deref,
F: FnOnce(&'a T::Target) -> R,
{
f(x.deref())
}
}
fn main() {} fn main() {}

View file

@ -0,0 +1,41 @@
// run-rustfix
#[warn(clippy::manual_range_contains)]
#[allow(unused)]
#[allow(clippy::no_effect)]
#[allow(clippy::short_circuit_statement)]
#[allow(clippy::unnecessary_operation)]
fn main() {
let x = 9_u32;
// order shouldn't matter
(8..12).contains(&x);
(21..42).contains(&x);
(1..100).contains(&x);
// also with inclusive ranges
(9..=99).contains(&x);
(1..=33).contains(&x);
(1..=999).contains(&x);
// and the outside
!(8..12).contains(&x);
!(21..42).contains(&x);
!(1..100).contains(&x);
// also with the outside of inclusive ranges
!(9..=99).contains(&x);
!(1..=33).contains(&x);
!(1..=999).contains(&x);
// not a range.contains
x > 8 && x < 12; // lower bound not inclusive
x < 8 && x <= 12; // same direction
x >= 12 && 12 >= x; // same bounds
x < 8 && x > 12; // wrong direction
x <= 8 || x >= 12;
x >= 8 || x >= 12;
x < 12 || 12 < x;
x >= 8 || x <= 12;
}

View file

@ -0,0 +1,41 @@
// run-rustfix
#[warn(clippy::manual_range_contains)]
#[allow(unused)]
#[allow(clippy::no_effect)]
#[allow(clippy::short_circuit_statement)]
#[allow(clippy::unnecessary_operation)]
fn main() {
let x = 9_u32;
// order shouldn't matter
x >= 8 && x < 12;
x < 42 && x >= 21;
100 > x && 1 <= x;
// also with inclusive ranges
x >= 9 && x <= 99;
x <= 33 && x >= 1;
999 >= x && 1 <= x;
// and the outside
x < 8 || x >= 12;
x >= 42 || x < 21;
100 <= x || 1 > x;
// also with the outside of inclusive ranges
x < 9 || x > 99;
x > 33 || x < 1;
999 < x || 1 > x;
// not a range.contains
x > 8 && x < 12; // lower bound not inclusive
x < 8 && x <= 12; // same direction
x >= 12 && 12 >= x; // same bounds
x < 8 && x > 12; // wrong direction
x <= 8 || x >= 12;
x >= 8 || x >= 12;
x < 12 || 12 < x;
x >= 8 || x <= 12;
}

View file

@ -0,0 +1,76 @@
error: manual `Range::contains` implementation
--> $DIR/range_contains.rs:12:5
|
LL | x >= 8 && x < 12;
| ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)`
|
= note: `-D clippy::manual-range-contains` implied by `-D warnings`
error: manual `Range::contains` implementation
--> $DIR/range_contains.rs:13:5
|
LL | x < 42 && x >= 21;
| ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)`
error: manual `Range::contains` implementation
--> $DIR/range_contains.rs:14:5
|
LL | 100 > x && 1 <= x;
| ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)`
error: manual `RangeInclusive::contains` implementation
--> $DIR/range_contains.rs:17:5
|
LL | x >= 9 && x <= 99;
| ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)`
error: manual `RangeInclusive::contains` implementation
--> $DIR/range_contains.rs:18:5
|
LL | x <= 33 && x >= 1;
| ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)`
error: manual `RangeInclusive::contains` implementation
--> $DIR/range_contains.rs:19:5
|
LL | 999 >= x && 1 <= x;
| ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)`
error: manual `!Range::contains` implementation
--> $DIR/range_contains.rs:22:5
|
LL | x < 8 || x >= 12;
| ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)`
error: manual `!Range::contains` implementation
--> $DIR/range_contains.rs:23:5
|
LL | x >= 42 || x < 21;
| ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)`
error: manual `!Range::contains` implementation
--> $DIR/range_contains.rs:24:5
|
LL | 100 <= x || 1 > x;
| ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)`
error: manual `!RangeInclusive::contains` implementation
--> $DIR/range_contains.rs:27:5
|
LL | x < 9 || x > 99;
| ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)`
error: manual `!RangeInclusive::contains` implementation
--> $DIR/range_contains.rs:28:5
|
LL | x > 33 || x < 1;
| ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)`
error: manual `!RangeInclusive::contains` implementation
--> $DIR/range_contains.rs:29:5
|
LL | 999 < x || 1 > x;
| ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)`
error: aborting due to 12 previous errors

View file

@ -0,0 +1,11 @@
// run-rustfix
// Tests from for_loop.rs that don't have suggestions
#[warn(clippy::single_element_loop)]
fn main() {
let item1 = 2;
{
let item = &item1;
println!("{}", item);
}
}

View file

@ -0,0 +1,10 @@
// run-rustfix
// Tests from for_loop.rs that don't have suggestions
#[warn(clippy::single_element_loop)]
fn main() {
let item1 = 2;
for item in &[item1] {
println!("{}", item);
}
}

View file

@ -0,0 +1,19 @@
error: for loop over a single element
--> $DIR/single_element_loop.rs:7:5
|
LL | / for item in &[item1] {
LL | | println!("{}", item);
LL | | }
| |_____^
|
= note: `-D clippy::single-element-loop` implied by `-D warnings`
help: try
|
LL | {
LL | let item = &item1;
LL | println!("{}", item);
LL | }
|
error: aborting due to previous error

View file

@ -1,5 +1,4 @@
#![warn(clippy::temporary_assignment)] #![warn(clippy::temporary_assignment)]
#![allow(const_item_mutation)]
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};

View file

@ -1,5 +1,5 @@
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:48:5 --> $DIR/temporary_assignment.rs:47:5
| |
LL | Struct { field: 0 }.field = 1; LL | Struct { field: 0 }.field = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1;
= note: `-D clippy::temporary-assignment` implied by `-D warnings` = note: `-D clippy::temporary-assignment` implied by `-D warnings`
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:49:5 --> $DIR/temporary_assignment.rs:48:5
| |
LL | / MultiStruct { LL | / MultiStruct {
LL | | structure: Struct { field: 0 }, LL | | structure: Struct { field: 0 },
@ -17,13 +17,13 @@ LL | | .field = 1;
| |______________^ | |______________^
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:54:5 --> $DIR/temporary_assignment.rs:53:5
| |
LL | ArrayStruct { array: [0] }.array[0] = 1; LL | ArrayStruct { array: [0] }.array[0] = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:55:5 --> $DIR/temporary_assignment.rs:54:5
| |
LL | (0, 0).0 = 1; LL | (0, 0).0 = 1;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^

View file

@ -0,0 +1,26 @@
#![warn(clippy::undropped_manually_drops)]
struct S;
fn main() {
let f = std::mem::drop;
let g = std::mem::ManuallyDrop::drop;
let mut manual1 = std::mem::ManuallyDrop::new(S);
let mut manual2 = std::mem::ManuallyDrop::new(S);
let mut manual3 = std::mem::ManuallyDrop::new(S);
let mut manual4 = std::mem::ManuallyDrop::new(S);
// These lines will not drop `S` and should be linted
drop(std::mem::ManuallyDrop::new(S));
drop(manual1);
// FIXME: this line is not linted, though it should be
f(manual2);
// These lines will drop `S` and should be okay.
unsafe {
std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
std::mem::ManuallyDrop::drop(&mut manual3);
g(&mut manual4);
}
}

View file

@ -0,0 +1,19 @@
error: the inner value of this ManuallyDrop will not be dropped
--> $DIR/undropped_manually_drops.rs:14:5
|
LL | drop(std::mem::ManuallyDrop::new(S));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::undropped-manually-drops` implied by `-D warnings`
= help: to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop
error: the inner value of this ManuallyDrop will not be dropped
--> $DIR/undropped_manually_drops.rs:15:5
|
LL | drop(manual1);
| ^^^^^^^^^^^^^
|
= help: to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop
error: aborting due to 2 previous errors

View file

@ -8,16 +8,31 @@ fn main() {
100_f32; 100_f32;
100_f64; 100_f64;
100_f64; 100_f64;
let _ = -100_f32;
let _ = -100_f64;
let _ = -100_f64;
// Should not trigger // Should not trigger
#[rustfmt::skip] #[rustfmt::skip]
let v = vec!(1); let v = vec!(1);
&v as &[i32]; &v as &[i32];
1.0 as f64;
1 as u64;
0x10 as f32; 0x10 as f32;
0o10 as f32; 0o10 as f32;
0b10 as f32; 0b10 as f32;
0x11 as f64; 0x11 as f64;
0o11 as f64; 0o11 as f64;
0b11 as f64; 0b11 as f64;
1_u32;
0x10_i32;
0b10_usize;
0o73_u16;
1_000_000_000_u32;
1.0_f64;
0.5_f32;
1.0 as u16;
let _ = -1_i32;
let _ = -1.0_f32;
} }

View file

@ -8,16 +8,31 @@ fn main() {
100 as f32; 100 as f32;
100 as f64; 100 as f64;
100_i32 as f64; 100_i32 as f64;
let _ = -100 as f32;
let _ = -100 as f64;
let _ = -100_i32 as f64;
// Should not trigger // Should not trigger
#[rustfmt::skip] #[rustfmt::skip]
let v = vec!(1); let v = vec!(1);
&v as &[i32]; &v as &[i32];
1.0 as f64;
1 as u64;
0x10 as f32; 0x10 as f32;
0o10 as f32; 0o10 as f32;
0b10 as f32; 0b10 as f32;
0x11 as f64; 0x11 as f64;
0o11 as f64; 0o11 as f64;
0b11 as f64; 0b11 as f64;
1 as u32;
0x10 as i32;
0b10 as usize;
0o73 as u16;
1_000_000_000 as u32;
1.0 as f64;
0.5 as f32;
1.0 as u16;
let _ = -1 as i32;
let _ = -1.0 as f32;
} }

View file

@ -18,5 +18,77 @@ error: casting integer literal to `f64` is unnecessary
LL | 100_i32 as f64; LL | 100_i32 as f64;
| ^^^^^^^^^^^^^^ help: try: `100_f64` | ^^^^^^^^^^^^^^ help: try: `100_f64`
error: aborting due to 3 previous errors error: casting integer literal to `f32` is unnecessary
--> $DIR/unnecessary_cast_fixable.rs:11:13
|
LL | let _ = -100 as f32;
| ^^^^^^^^^^^ help: try: `-100_f32`
error: casting integer literal to `f64` is unnecessary
--> $DIR/unnecessary_cast_fixable.rs:12:13
|
LL | let _ = -100 as f64;
| ^^^^^^^^^^^ help: try: `-100_f64`
error: casting integer literal to `f64` is unnecessary
--> $DIR/unnecessary_cast_fixable.rs:13:13
|
LL | let _ = -100_i32 as f64;
| ^^^^^^^^^^^^^^^ help: try: `-100_f64`
error: casting integer literal to `u32` is unnecessary
--> $DIR/unnecessary_cast_fixable.rs:25:5
|
LL | 1 as u32;
| ^^^^^^^^ help: try: `1_u32`
error: casting integer literal to `i32` is unnecessary
--> $DIR/unnecessary_cast_fixable.rs:26:5
|
LL | 0x10 as i32;
| ^^^^^^^^^^^ help: try: `0x10_i32`
error: casting integer literal to `usize` is unnecessary
--> $DIR/unnecessary_cast_fixable.rs:27:5
|
LL | 0b10 as usize;
| ^^^^^^^^^^^^^ help: try: `0b10_usize`
error: casting integer literal to `u16` is unnecessary
--> $DIR/unnecessary_cast_fixable.rs:28:5
|
LL | 0o73 as u16;
| ^^^^^^^^^^^ help: try: `0o73_u16`
error: casting integer literal to `u32` is unnecessary
--> $DIR/unnecessary_cast_fixable.rs:29:5
|
LL | 1_000_000_000 as u32;
| ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32`
error: casting float literal to `f64` is unnecessary
--> $DIR/unnecessary_cast_fixable.rs:31:5
|
LL | 1.0 as f64;
| ^^^^^^^^^^ help: try: `1.0_f64`
error: casting float literal to `f32` is unnecessary
--> $DIR/unnecessary_cast_fixable.rs:32:5
|
LL | 0.5 as f32;
| ^^^^^^^^^^ help: try: `0.5_f32`
error: casting integer literal to `i32` is unnecessary
--> $DIR/unnecessary_cast_fixable.rs:36:13
|
LL | let _ = -1 as i32;
| ^^^^^^^^^ help: try: `-1_i32`
error: casting float literal to `f32` is unnecessary
--> $DIR/unnecessary_cast_fixable.rs:37:13
|
LL | let _ = -1.0 as f32;
| ^^^^^^^^^^^ help: try: `-1.0_f32`
error: aborting due to 15 previous errors

View file

@ -1,7 +0,0 @@
#!/bin/sh
CARGO_TARGET_DIR=$(pwd)/target/
export CARGO_TARGET_DIR
echo 'Deprecated! `util/dev` usage is deprecated, please use `cargo dev` instead.'
cd clippy_dev && cargo run -- "$@"