mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 23:04:33 +00:00
Merge commit '645ef505da378b6f810b1567806d1bcc2856395f' into clippyup
This commit is contained in:
parent
a1bb10e9b8
commit
50419118b4
20
.github/driver.sh
vendored
20
.github/driver.sh
vendored
@ -7,9 +7,9 @@ sysroot=$(./target/debug/clippy-driver --print sysroot)
|
||||
test "$sysroot" = "$(rustc --print sysroot)"
|
||||
|
||||
if [[ ${OS} == "Windows" ]]; then
|
||||
desired_sysroot=C:/tmp
|
||||
desired_sysroot=C:/tmp
|
||||
else
|
||||
desired_sysroot=/tmp
|
||||
desired_sysroot=/tmp
|
||||
fi
|
||||
sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot)
|
||||
test "$sysroot" = $desired_sysroot
|
||||
@ -22,20 +22,18 @@ unset CARGO_MANIFEST_DIR
|
||||
|
||||
# Run a lint and make sure it produces the expected output. It's also expected to exit with code 1
|
||||
# FIXME: How to match the clippy invocation in compile-test.rs?
|
||||
./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/cast.rs 2> cast.stderr && exit 1
|
||||
sed -e "s,tests/ui,\$DIR," -e "/= help/d" cast.stderr > normalized.stderr
|
||||
diff normalized.stderr tests/ui/cast.stderr
|
||||
|
||||
./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2>double_neg.stderr && exit 1
|
||||
sed -e "s,tests/ui,\$DIR," -e "/= help/d" double_neg.stderr >normalized.stderr
|
||||
diff normalized.stderr tests/ui/double_neg.stderr
|
||||
|
||||
# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
|
||||
SYSROOT=`rustc --print sysroot`
|
||||
SYSROOT=$(rustc --print sysroot)
|
||||
diff <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose)
|
||||
|
||||
|
||||
echo "fn main() {}" > target/driver_test.rs
|
||||
echo "fn main() {}" >target/driver_test.rs
|
||||
# we can't run 2 rustcs on the same file at the same time
|
||||
CLIPPY=`LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc`
|
||||
RUSTC=`rustc ./target/driver_test.rs`
|
||||
CLIPPY=$(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc)
|
||||
RUSTC=$(rustc ./target/driver_test.rs)
|
||||
diff <($CLIPPY) <($RUSTC)
|
||||
|
||||
# TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR
|
||||
|
@ -1632,6 +1632,7 @@ Released 2018-09-13
|
||||
[`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
|
||||
[`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
|
||||
[`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
|
||||
@ -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_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_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_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
|
||||
@ -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_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_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_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
|
||||
@ -1841,6 +1844,7 @@ Released 2018-09-13
|
||||
[`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_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
|
||||
[`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
|
||||
@ -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_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_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_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
|
||||
@ -1979,6 +1984,7 @@ Released 2018-09-13
|
||||
[`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_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
|
||||
[`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
|
||||
@ -2011,6 +2017,7 @@ Released 2018-09-13
|
||||
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
|
||||
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
|
||||
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
|
||||
[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
|
||||
[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
|
||||
[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
|
||||
[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
|
||||
|
@ -45,13 +45,52 @@ declare_clippy_lint! {
|
||||
/// }
|
||||
/// ```
|
||||
pub AWAIT_HOLDING_LOCK,
|
||||
pedantic,
|
||||
correctness,
|
||||
"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<'_>) {
|
||||
use AsyncGeneratorKind::{Block, Closure, Fn};
|
||||
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",
|
||||
);
|
||||
}
|
||||
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_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)
|
||||
}
|
@ -28,7 +28,6 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn somefunc() -> bool { true };
|
||||
///
|
||||
/// // Bad
|
||||
/// if { let x = somefunc(); x } { /* ... */ }
|
||||
///
|
||||
|
@ -1,13 +1,8 @@
|
||||
use crate::utils::{eq_expr_value, in_macro, SpanlessEq, SpanlessHash};
|
||||
use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind};
|
||||
use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
|
||||
use crate::utils::{get_parent_expr, higher, if_sequence, span_lint_and_note};
|
||||
use rustc_hir::{Block, Expr};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{Ty, TyS};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::hash::BuildHasherDefault;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for consecutive `if`s with the same condition.
|
||||
@ -108,48 +103,7 @@ declare_clippy_lint! {
|
||||
"`if` with the same `then` and `else` blocks"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `match` with identical arm bodies.
|
||||
///
|
||||
/// **Why is this bad?** This is probably a copy & paste error. If arm bodies
|
||||
/// are the same on purpose, you can factor them
|
||||
/// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
|
||||
///
|
||||
/// **Known problems:** False positive possible with order dependent `match`
|
||||
/// (see issue
|
||||
/// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar => bar(),
|
||||
/// Quz => quz(),
|
||||
/// Baz => bar(), // <= oops
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This should probably be
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar => bar(),
|
||||
/// Quz => quz(),
|
||||
/// Baz => baz(), // <= fixed
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// or if the original code was not a typo:
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar | Baz => bar(), // <= shows the intent better
|
||||
/// Quz => quz(),
|
||||
/// }
|
||||
/// ```
|
||||
pub MATCH_SAME_ARMS,
|
||||
pedantic,
|
||||
"`match` with identical arm bodies"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]);
|
||||
declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
@ -167,7 +121,6 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
|
||||
lint_same_then_else(cx, &blocks);
|
||||
lint_same_cond(cx, &conds);
|
||||
lint_same_fns_in_if_cond(cx, &conds);
|
||||
lint_match_arms(cx, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -243,122 +196,6 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `MATCH_SAME_ARMS`.
|
||||
fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||
fn same_bindings<'tcx>(lhs: &FxHashMap<Symbol, Ty<'tcx>>, rhs: &FxHashMap<Symbol, Ty<'tcx>>) -> bool {
|
||||
lhs.len() == rhs.len()
|
||||
&& lhs
|
||||
.iter()
|
||||
.all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty)))
|
||||
}
|
||||
|
||||
if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
|
||||
let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
|
||||
let mut h = SpanlessHash::new(cx);
|
||||
h.hash_expr(&arm.body);
|
||||
h.finish()
|
||||
};
|
||||
|
||||
let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
|
||||
let min_index = usize::min(lindex, rindex);
|
||||
let max_index = usize::max(lindex, rindex);
|
||||
|
||||
// Arms with a guard are ignored, those can’t always be merged together
|
||||
// This is also the case for arms in-between each there is an arm with a guard
|
||||
(min_index..=max_index).all(|index| arms[index].guard.is_none()) &&
|
||||
SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) &&
|
||||
// all patterns should have the same bindings
|
||||
same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat))
|
||||
};
|
||||
|
||||
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
|
||||
for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MATCH_SAME_ARMS,
|
||||
j.body.span,
|
||||
"this `match` has identical arm bodies",
|
||||
|diag| {
|
||||
diag.span_note(i.body.span, "same as this");
|
||||
|
||||
// Note: this does not use `span_suggestion` on purpose:
|
||||
// there is no clean way
|
||||
// to remove the other arm. Building a span and suggest to replace it to ""
|
||||
// makes an even more confusing error message. Also in order not to make up a
|
||||
// span for the whole pattern, the suggestion is only shown when there is only
|
||||
// one pattern. The user should know about `|` if they are already using it…
|
||||
|
||||
let lhs = snippet(cx, i.pat.span, "<pat1>");
|
||||
let rhs = snippet(cx, j.pat.span, "<pat2>");
|
||||
|
||||
if let PatKind::Wild = j.pat.kind {
|
||||
// if the last arm is _, then i could be integrated into _
|
||||
// note that i.pat cannot be _, because that would mean that we're
|
||||
// hiding all the subsequent arms, and rust won't compile
|
||||
diag.span_note(
|
||||
i.body.span,
|
||||
&format!(
|
||||
"`{}` has the same arm body as the `_` wildcard, consider removing it",
|
||||
lhs
|
||||
),
|
||||
);
|
||||
} else {
|
||||
diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the list of bindings in a pattern.
|
||||
fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap<Symbol, Ty<'tcx>> {
|
||||
fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap<Symbol, Ty<'tcx>>) {
|
||||
match pat.kind {
|
||||
PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
|
||||
PatKind::TupleStruct(_, pats, _) => {
|
||||
for pat in pats {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
},
|
||||
PatKind::Binding(.., ident, ref as_pat) => {
|
||||
if let Entry::Vacant(v) = map.entry(ident.name) {
|
||||
v.insert(cx.typeck_results().pat_ty(pat));
|
||||
}
|
||||
if let Some(ref as_pat) = *as_pat {
|
||||
bindings_impl(cx, as_pat, map);
|
||||
}
|
||||
},
|
||||
PatKind::Or(fields) | PatKind::Tuple(fields, _) => {
|
||||
for pat in fields {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
},
|
||||
PatKind::Struct(_, fields, _) => {
|
||||
for pat in fields {
|
||||
bindings_impl(cx, &pat.pat, map);
|
||||
}
|
||||
},
|
||||
PatKind::Slice(lhs, ref mid, rhs) => {
|
||||
for pat in lhs {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
if let Some(ref mid) = *mid {
|
||||
bindings_impl(cx, mid, map);
|
||||
}
|
||||
for pat in rhs {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
},
|
||||
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (),
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = FxHashMap::default();
|
||||
bindings_impl(cx, pat, &mut result);
|
||||
result
|
||||
}
|
||||
|
||||
fn search_same_sequenced<T, Eq>(exprs: &[T], eq: Eq) -> Option<(&T, &T)>
|
||||
where
|
||||
Eq: Fn(&T, &T) -> bool,
|
||||
@ -370,47 +207,3 @@ where
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn search_common_cases<'a, T, Eq>(exprs: &'a [T], eq: &Eq) -> Option<(&'a T, &'a T)>
|
||||
where
|
||||
Eq: Fn(&T, &T) -> bool,
|
||||
{
|
||||
if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) {
|
||||
Some((&exprs[0], &exprs[1]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
|
||||
where
|
||||
Hash: Fn(&T) -> u64,
|
||||
Eq: Fn(&T, &T) -> bool,
|
||||
{
|
||||
if let Some(expr) = search_common_cases(&exprs, &eq) {
|
||||
return vec![expr];
|
||||
}
|
||||
|
||||
let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
|
||||
|
||||
let mut map: FxHashMap<_, Vec<&_>> =
|
||||
FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
|
||||
|
||||
for expr in exprs {
|
||||
match map.entry(hash(expr)) {
|
||||
Entry::Occupied(mut o) => {
|
||||
for o in o.get() {
|
||||
if eq(o, expr) {
|
||||
match_expr_list.push((o, expr));
|
||||
}
|
||||
}
|
||||
o.get_mut().push(expr);
|
||||
},
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(vec![expr]);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
match_expr_list
|
||||
}
|
||||
|
@ -172,3 +172,12 @@ declare_deprecated_lint! {
|
||||
pub DROP_BOUNDS,
|
||||
"this lint has been uplifted to rustc and is now called `drop_bounds`"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This lint has been uplifted to rustc and is now called
|
||||
/// `temporary_cstring_as_ptr`.
|
||||
pub TEMPORARY_CSTRING_AS_PTR,
|
||||
"this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`"
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::utils::paths;
|
||||
use crate::utils::{
|
||||
get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help,
|
||||
get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_def_path, match_path, span_lint_and_help,
|
||||
span_lint_and_note, span_lint_and_then,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
@ -193,10 +193,9 @@ fn check_hash_peq<'tcx>(
|
||||
hash_is_automatically_derived: bool,
|
||||
) {
|
||||
if_chain! {
|
||||
if match_path(&trait_ref.path, &paths::HASH);
|
||||
if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
|
||||
if let Some(def_id) = &trait_ref.trait_def_id();
|
||||
if !def_id.is_local();
|
||||
if let Some(def_id) = trait_ref.trait_def_id();
|
||||
if match_def_path(cx, def_id, &paths::HASH);
|
||||
then {
|
||||
// Look for the PartialEq implementations for `ty`
|
||||
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
|
||||
@ -352,7 +351,8 @@ fn check_unsafe_derive_deserialize<'tcx>(
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE);
|
||||
if let Some(trait_def_id) = trait_ref.trait_def_id();
|
||||
if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
|
||||
if let ty::Adt(def, _) = ty.kind();
|
||||
if let Some(local_def_id) = def.did.as_local();
|
||||
let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
|
||||
|
@ -29,7 +29,6 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # fn foo(bar: usize) {}
|
||||
///
|
||||
/// // Bad
|
||||
/// let x = Box::new(1);
|
||||
/// foo(*x);
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::utils::{match_qpath, paths, span_lint_and_then, sugg};
|
||||
use crate::utils::{match_def_path, paths, span_lint_and_then, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::util::parser::AssocOp;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
@ -76,7 +77,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs {
|
||||
|
||||
// right hand side matches either f32::EPSILON or f64::EPSILON
|
||||
if let ExprKind::Path(ref epsilon_path) = rhs.kind;
|
||||
if match_qpath(epsilon_path, &paths::F32_EPSILON) || match_qpath(epsilon_path, &paths::F64_EPSILON);
|
||||
if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id);
|
||||
if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON);
|
||||
|
||||
// values of the substractions on the left hand side are of the type float
|
||||
let t_val_l = cx.typeck_results().expr_ty(val_l);
|
||||
|
@ -579,9 +579,8 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet<
|
||||
if let hir::PatKind::Wild = pat.kind {
|
||||
return false; // ignore `_` patterns
|
||||
}
|
||||
let def_id = pat.hir_id.owner.to_def_id();
|
||||
if cx.tcx.has_typeck_results(def_id) {
|
||||
is_mutable_ty(cx, &cx.tcx.typeck(def_id.expect_local()).pat_ty(pat), pat.span, tys)
|
||||
if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) {
|
||||
is_mutable_ty(cx, &cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@ -694,11 +693,10 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
|
||||
Call(_, args) | MethodCall(_, _, args, _) => {
|
||||
let mut tys = FxHashSet::default();
|
||||
for arg in args {
|
||||
let def_id = arg.hir_id.owner.to_def_id();
|
||||
if self.cx.tcx.has_typeck_results(def_id)
|
||||
if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
|
||||
&& is_mutable_ty(
|
||||
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,
|
||||
&mut tys,
|
||||
)
|
||||
|
@ -160,7 +160,7 @@ mod assign_ops;
|
||||
mod async_yields_async;
|
||||
mod atomic_ordering;
|
||||
mod attrs;
|
||||
mod await_holding_lock;
|
||||
mod await_holding_invalid;
|
||||
mod bit_mask;
|
||||
mod blacklisted_name;
|
||||
mod blocks_in_if_conditions;
|
||||
@ -255,6 +255,7 @@ mod modulo_arithmetic;
|
||||
mod multiple_crate_versions;
|
||||
mod mut_key;
|
||||
mod mut_mut;
|
||||
mod mut_mutex_lock;
|
||||
mod mut_reference;
|
||||
mod mutable_debug_assertion;
|
||||
mod mutex_atomic;
|
||||
@ -278,6 +279,7 @@ mod overflow_check_conditional;
|
||||
mod panic_in_result_fn;
|
||||
mod panic_unimplemented;
|
||||
mod partialeq_ne_impl;
|
||||
mod pass_by_ref_or_value;
|
||||
mod path_buf_push_overwrite;
|
||||
mod pattern_type_mismatch;
|
||||
mod precedence;
|
||||
@ -311,9 +313,9 @@ mod to_string_in_display;
|
||||
mod trait_bounds;
|
||||
mod transmute;
|
||||
mod transmuting_null;
|
||||
mod trivially_copy_pass_by_ref;
|
||||
mod try_err;
|
||||
mod types;
|
||||
mod undropped_manually_drops;
|
||||
mod unicode;
|
||||
mod unit_return_expecting_ord;
|
||||
mod unnamed_address;
|
||||
@ -486,6 +488,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
"clippy::drop_bounds",
|
||||
"this lint has been uplifted to rustc and is now called `drop_bounds`",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::temporary_cstring_as_ptr",
|
||||
"this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`",
|
||||
);
|
||||
// end deprecated lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
// begin register lints, do not remove this comment, it’s used in `update_lints`
|
||||
@ -509,7 +515,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&attrs::MISMATCHED_TARGET_OS,
|
||||
&attrs::UNKNOWN_CLIPPY_LINTS,
|
||||
&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::INEFFECTIVE_BIT_MASK,
|
||||
&bit_mask::VERBOSE_BIT_MASK,
|
||||
@ -525,7 +532,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&comparison_chain::COMPARISON_CHAIN,
|
||||
&copies::IFS_SAME_COND,
|
||||
&copies::IF_SAME_THEN_ELSE,
|
||||
&copies::MATCH_SAME_ARMS,
|
||||
&copies::SAME_FUNCTIONS_IN_IF_CONDITION,
|
||||
©_iterator::COPY_ITERATOR,
|
||||
&create_dir::CREATE_DIR,
|
||||
@ -620,6 +626,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&literal_representation::LARGE_DIGIT_GROUPS,
|
||||
&literal_representation::MISTYPED_LITERAL_SUFFIXES,
|
||||
&literal_representation::UNREADABLE_LITERAL,
|
||||
&literal_representation::UNUSUAL_BYTE_GROUPINGS,
|
||||
&loops::EMPTY_LOOP,
|
||||
&loops::EXPLICIT_COUNTER_LOOP,
|
||||
&loops::EXPLICIT_INTO_ITER_LOOP,
|
||||
@ -633,6 +640,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&loops::NEEDLESS_RANGE_LOOP,
|
||||
&loops::NEVER_LOOP,
|
||||
&loops::SAME_ITEM_PUSH,
|
||||
&loops::SINGLE_ELEMENT_LOOP,
|
||||
&loops::WHILE_IMMUTABLE_CONDITION,
|
||||
&loops::WHILE_LET_LOOP,
|
||||
&loops::WHILE_LET_ON_ITERATOR,
|
||||
@ -654,6 +662,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&matches::MATCH_LIKE_MATCHES_MACRO,
|
||||
&matches::MATCH_OVERLAPPING_ARM,
|
||||
&matches::MATCH_REF_PATS,
|
||||
&matches::MATCH_SAME_ARMS,
|
||||
&matches::MATCH_SINGLE_BINDING,
|
||||
&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
|
||||
&matches::MATCH_WILD_ERR_ARM,
|
||||
@ -742,6 +751,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
|
||||
&mut_key::MUTABLE_KEY_TYPE,
|
||||
&mut_mut::MUT_MUT,
|
||||
&mut_mutex_lock::MUT_MUTEX_LOCK,
|
||||
&mut_reference::UNNECESSARY_MUT_PASSED,
|
||||
&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
|
||||
&mutex_atomic::MUTEX_ATOMIC,
|
||||
@ -775,6 +785,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&panic_unimplemented::UNIMPLEMENTED,
|
||||
&panic_unimplemented::UNREACHABLE,
|
||||
&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,
|
||||
&pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
|
||||
&precedence::PRECEDENCE,
|
||||
@ -784,6 +796,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&ptr_eq::PTR_EQ,
|
||||
&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
|
||||
&question_mark::QUESTION_MARK,
|
||||
&ranges::MANUAL_RANGE_CONTAINS,
|
||||
&ranges::RANGE_MINUS_ONE,
|
||||
&ranges::RANGE_PLUS_ONE,
|
||||
&ranges::RANGE_ZIP_WITH_LEN,
|
||||
@ -834,7 +847,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&transmute::USELESS_TRANSMUTE,
|
||||
&transmute::WRONG_TRANSMUTE,
|
||||
&transmuting_null::TRANSMUTING_NULL,
|
||||
&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF,
|
||||
&try_err::TRY_ERR,
|
||||
&types::ABSURD_EXTREME_COMPARISONS,
|
||||
&types::BORROWED_BOX,
|
||||
@ -861,6 +873,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&types::UNIT_CMP,
|
||||
&types::UNNECESSARY_CAST,
|
||||
&types::VEC_BOX,
|
||||
&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
|
||||
&unicode::INVISIBLE_CHARACTERS,
|
||||
&unicode::NON_ASCII_LITERAL,
|
||||
&unicode::UNICODE_NOT_NFC,
|
||||
@ -882,6 +895,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
&utils::internal_lints::COMPILER_LINT_FUNCTIONS,
|
||||
&utils::internal_lints::DEFAULT_LINT,
|
||||
&utils::internal_lints::INVALID_PATHS,
|
||||
&utils::internal_lints::LINT_WITHOUT_LINT_PASS,
|
||||
&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
|
||||
&utils::internal_lints::OUTER_EXPN_EXPN_DATA,
|
||||
@ -904,11 +918,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
]);
|
||||
// end register lints, do not remove this comment, it’s 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 utils::internal_lints::CompilerLintFunctions::new());
|
||||
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
|
||||
store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
|
||||
store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
|
||||
store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
|
||||
store.register_late_pass(|| box utils::author::Author);
|
||||
let vec_box_size_threshold = conf.vec_box_size_threshold;
|
||||
@ -1008,11 +1023,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(|| box explicit_write::ExplicitWrite);
|
||||
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.pass_by_value_size_limit,
|
||||
&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 use_self::UseSelf);
|
||||
store.register_late_pass(|| box bytecount::ByteCount);
|
||||
@ -1108,6 +1124,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 utils::internal_lints::CollapsibleCalls);
|
||||
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_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
|
||||
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
|
||||
@ -1136,6 +1153,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_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax);
|
||||
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![
|
||||
@ -1187,10 +1205,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
|
||||
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(&attrs::INLINE_ALWAYS),
|
||||
LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK),
|
||||
LintId::of(&bit_mask::VERBOSE_BIT_MASK),
|
||||
LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
|
||||
LintId::of(&copies::MATCH_SAME_ARMS),
|
||||
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
|
||||
LintId::of(©_iterator::COPY_ITERATOR),
|
||||
LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS),
|
||||
@ -1220,6 +1236,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&map_err_ignore::MAP_ERR_IGNORE),
|
||||
LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
|
||||
LintId::of(&matches::MATCH_BOOL),
|
||||
LintId::of(&matches::MATCH_SAME_ARMS),
|
||||
LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
|
||||
LintId::of(&matches::MATCH_WILD_ERR_ARM),
|
||||
LintId::of(&matches::SINGLE_MATCH_ELSE),
|
||||
@ -1236,13 +1253,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(&non_expressive_names::SIMILAR_NAMES),
|
||||
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_PLUS_ONE),
|
||||
LintId::of(&shadow::SHADOW_UNRELATED),
|
||||
LintId::of(&strings::STRING_ADD_ASSIGN),
|
||||
LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
|
||||
LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
|
||||
LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF),
|
||||
LintId::of(&types::CAST_LOSSLESS),
|
||||
LintId::of(&types::CAST_POSSIBLE_TRUNCATION),
|
||||
LintId::of(&types::CAST_POSSIBLE_WRAP),
|
||||
@ -1267,6 +1285,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
|
||||
LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
|
||||
LintId::of(&utils::internal_lints::DEFAULT_LINT),
|
||||
LintId::of(&utils::internal_lints::INVALID_PATHS),
|
||||
LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
|
||||
LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
|
||||
LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA),
|
||||
@ -1286,6 +1305,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&attrs::MISMATCHED_TARGET_OS),
|
||||
LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
|
||||
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::INEFFECTIVE_BIT_MASK),
|
||||
LintId::of(&blacklisted_name::BLACKLISTED_NAME),
|
||||
@ -1351,6 +1372,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
|
||||
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
|
||||
LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES),
|
||||
LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS),
|
||||
LintId::of(&loops::EMPTY_LOOP),
|
||||
LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
|
||||
LintId::of(&loops::FOR_KV_MAP),
|
||||
@ -1362,6 +1384,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
|
||||
LintId::of(&loops::NEVER_LOOP),
|
||||
LintId::of(&loops::SAME_ITEM_PUSH),
|
||||
LintId::of(&loops::SINGLE_ELEMENT_LOOP),
|
||||
LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
|
||||
LintId::of(&loops::WHILE_LET_LOOP),
|
||||
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
|
||||
@ -1439,6 +1462,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN),
|
||||
LintId::of(&misc_early::ZERO_PREFIXED_LITERAL),
|
||||
LintId::of(&mut_key::MUTABLE_KEY_TYPE),
|
||||
LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK),
|
||||
LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
|
||||
LintId::of(&mutex_atomic::MUTEX_ATOMIC),
|
||||
LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
|
||||
@ -1467,6 +1491,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&ptr_eq::PTR_EQ),
|
||||
LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
|
||||
LintId::of(&question_mark::QUESTION_MARK),
|
||||
LintId::of(&ranges::MANUAL_RANGE_CONTAINS),
|
||||
LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(&ranges::REVERSED_EMPTY_RANGES),
|
||||
LintId::of(&redundant_clone::REDUNDANT_CLONE),
|
||||
@ -1519,6 +1544,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&types::UNIT_CMP),
|
||||
LintId::of(&types::UNNECESSARY_CAST),
|
||||
LintId::of(&types::VEC_BOX),
|
||||
LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
|
||||
LintId::of(&unicode::INVISIBLE_CHARACTERS),
|
||||
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
|
||||
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
|
||||
@ -1568,6 +1594,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
|
||||
LintId::of(&len_zero::LEN_ZERO),
|
||||
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
|
||||
LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS),
|
||||
LintId::of(&loops::EMPTY_LOOP),
|
||||
LintId::of(&loops::FOR_KV_MAP),
|
||||
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
|
||||
@ -1610,6 +1637,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
|
||||
LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS),
|
||||
LintId::of(&misc_early::REDUNDANT_PATTERN),
|
||||
LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK),
|
||||
LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
|
||||
LintId::of(&neg_multiply::NEG_MULTIPLY),
|
||||
LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT),
|
||||
@ -1622,6 +1650,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&ptr::PTR_ARG),
|
||||
LintId::of(&ptr_eq::PTR_EQ),
|
||||
LintId::of(&question_mark::QUESTION_MARK),
|
||||
LintId::of(&ranges::MANUAL_RANGE_CONTAINS),
|
||||
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
|
||||
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
|
||||
LintId::of(®ex::TRIVIAL_REGEX),
|
||||
@ -1662,6 +1691,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
|
||||
LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
|
||||
LintId::of(&loops::MUT_RANGE_BOUND),
|
||||
LintId::of(&loops::SINGLE_ELEMENT_LOOP),
|
||||
LintId::of(&loops::WHILE_LET_LOOP),
|
||||
LintId::of(&manual_strip::MANUAL_STRIP),
|
||||
LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR),
|
||||
@ -1731,6 +1761,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&attrs::DEPRECATED_SEMVER),
|
||||
LintId::of(&attrs::MISMATCHED_TARGET_OS),
|
||||
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::INEFFECTIVE_BIT_MASK),
|
||||
LintId::of(&booleans::LOGIC_BUG),
|
||||
@ -1787,6 +1819,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(&types::CAST_REF_TO_MUT),
|
||||
LintId::of(&types::UNIT_CMP),
|
||||
LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
|
||||
LintId::of(&unicode::INVISIBLE_CHARACTERS),
|
||||
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
|
||||
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
|
||||
|
@ -414,7 +414,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
|
||||
let mut visitor = RefVisitor::new(cx);
|
||||
// walk the type F, it may not contain LT refs
|
||||
walk_ty(&mut visitor, &pred.bounded_ty);
|
||||
if !visitor.lts.is_empty() {
|
||||
if !visitor.all_lts().is_empty() {
|
||||
return true;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// 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) => {
|
||||
let mut visitor = RefVisitor::new(cx);
|
||||
|
@ -82,6 +82,25 @@ declare_clippy_lint! {
|
||||
"integer literals with digits grouped inconsistently"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if hexadecimal or binary literals are not grouped
|
||||
/// by nibble or byte.
|
||||
///
|
||||
/// **Why is this bad?** Negatively impacts readability.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: u32 = 0xFFF_FFF;
|
||||
/// let y: u8 = 0b01_011_101;
|
||||
/// ```
|
||||
pub UNUSUAL_BYTE_GROUPINGS,
|
||||
style,
|
||||
"binary or hex literals that aren't grouped by four"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if the digits of an integral or floating-point
|
||||
/// constant are grouped into groups that
|
||||
@ -125,6 +144,7 @@ enum WarningType {
|
||||
LargeDigitGroups,
|
||||
DecimalRepresentation,
|
||||
MistypedLiteralSuffix,
|
||||
UnusualByteGroupings,
|
||||
}
|
||||
|
||||
impl WarningType {
|
||||
@ -175,6 +195,15 @@ impl WarningType {
|
||||
suggested_format,
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
Self::UnusualByteGroupings => span_lint_and_sugg(
|
||||
cx,
|
||||
UNUSUAL_BYTE_GROUPINGS,
|
||||
span,
|
||||
"digits of hex or binary literal not grouped by four",
|
||||
"consider",
|
||||
suggested_format,
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -184,6 +213,7 @@ declare_lint_pass!(LiteralDigitGrouping => [
|
||||
INCONSISTENT_DIGIT_GROUPING,
|
||||
LARGE_DIGIT_GROUPS,
|
||||
MISTYPED_LITERAL_SUFFIXES,
|
||||
UNUSUAL_BYTE_GROUPINGS,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for LiteralDigitGrouping {
|
||||
@ -217,9 +247,9 @@ impl LiteralDigitGrouping {
|
||||
|
||||
let result = (|| {
|
||||
|
||||
let integral_group_size = Self::get_group_size(num_lit.integer.split('_'))?;
|
||||
let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix)?;
|
||||
if let Some(fraction) = num_lit.fraction {
|
||||
let fractional_group_size = Self::get_group_size(fraction.rsplit('_'))?;
|
||||
let fractional_group_size = Self::get_group_size(fraction.rsplit('_'), num_lit.radix)?;
|
||||
|
||||
let consistent = Self::parts_consistent(integral_group_size,
|
||||
fractional_group_size,
|
||||
@ -229,6 +259,7 @@ impl LiteralDigitGrouping {
|
||||
return Err(WarningType::InconsistentDigitGrouping);
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})();
|
||||
|
||||
@ -237,6 +268,7 @@ impl LiteralDigitGrouping {
|
||||
let should_warn = match warning_type {
|
||||
| WarningType::UnreadableLiteral
|
||||
| WarningType::InconsistentDigitGrouping
|
||||
| WarningType::UnusualByteGroupings
|
||||
| WarningType::LargeDigitGroups => {
|
||||
!in_macro(lit.span)
|
||||
}
|
||||
@ -331,11 +363,15 @@ impl LiteralDigitGrouping {
|
||||
|
||||
/// Returns the size of the digit groups (or None if ungrouped) if successful,
|
||||
/// otherwise returns a `WarningType` for linting.
|
||||
fn get_group_size<'a>(groups: impl Iterator<Item = &'a str>) -> Result<Option<usize>, WarningType> {
|
||||
fn get_group_size<'a>(groups: impl Iterator<Item = &'a str>, radix: Radix) -> Result<Option<usize>, WarningType> {
|
||||
let mut groups = groups.map(str::len);
|
||||
|
||||
let first = groups.next().expect("At least one group");
|
||||
|
||||
if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 && i != 2) {
|
||||
return Err(WarningType::UnusualByteGroupings);
|
||||
}
|
||||
|
||||
if let Some(second) = groups.next() {
|
||||
if !groups.all(|x| x == second) || first > second {
|
||||
Err(WarningType::InconsistentDigitGrouping)
|
||||
|
@ -4,9 +4,10 @@ use crate::utils::sugg::Sugg;
|
||||
use crate::utils::usage::{is_unused, mutated_variables};
|
||||
use crate::utils::{
|
||||
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,
|
||||
match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability, snippet_with_macro_callsite,
|
||||
span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq,
|
||||
indent_of, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment,
|
||||
match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, snippet,
|
||||
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 rustc_ast::ast;
|
||||
@ -293,9 +294,24 @@ declare_clippy_lint! {
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for empty `loop` expressions.
|
||||
///
|
||||
/// **Why is this bad?** Those busy loops burn CPU cycles without doing
|
||||
/// anything. Think of the environment and either block on something or at least
|
||||
/// make the thread sleep for some microseconds.
|
||||
/// **Why is this bad?** These busy loops burn CPU cycles without doing
|
||||
/// anything. It is _almost always_ a better idea to `panic!` than to have
|
||||
/// 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.
|
||||
///
|
||||
@ -452,6 +468,31 @@ declare_clippy_lint! {
|
||||
"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 => [
|
||||
MANUAL_MEMCPY,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
@ -469,6 +510,7 @@ declare_lint_pass!(Loops => [
|
||||
MUT_RANGE_BOUND,
|
||||
WHILE_IMMUTABLE_CONDITION,
|
||||
SAME_ITEM_PUSH,
|
||||
SINGLE_ELEMENT_LOOP,
|
||||
]);
|
||||
|
||||
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)
|
||||
if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind {
|
||||
// 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()) {
|
||||
span_lint(
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
EMPTY_LOOP,
|
||||
expr.span,
|
||||
"empty `loop {}` detected. You may want to either use `panic!()` or add \
|
||||
`std::thread::sleep(..);` to the loop body.",
|
||||
"empty `loop {}` wastes CPU cycles",
|
||||
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_over_map_kv(cx, pat, arg, body, expr);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
hir_id_low: Option<HirId>,
|
||||
@ -1969,12 +2051,11 @@ fn check_for_mutation<'tcx>(
|
||||
span_low: None,
|
||||
span_high: None,
|
||||
};
|
||||
let def_id = body.hir_id.owner.to_def_id();
|
||||
cx.tcx.infer_ctxt().enter(|infcx| {
|
||||
ExprUseVisitor::new(
|
||||
&mut delegate,
|
||||
&infcx,
|
||||
def_id.expect_local(),
|
||||
body.hir_id.owner,
|
||||
cx.param_env,
|
||||
cx.typeck_results(),
|
||||
)
|
||||
@ -2920,7 +3001,14 @@ impl IterFunction {
|
||||
IterFunctionKind::IntoIter => String::new(),
|
||||
IterFunctionKind::Len => String::from(".count()"),
|
||||
IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
|
||||
IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")),
|
||||
IterFunctionKind::Contains(span) => {
|
||||
let s = snippet(cx, *span, "..");
|
||||
if let Some(stripped) = s.strip_prefix('&') {
|
||||
format!(".any(|x| x == {})", stripped)
|
||||
} else {
|
||||
format!(".any(|x| x == *{})", s)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
fn get_suggestion_text(&self) -> &'static str {
|
||||
|
@ -1,8 +1,9 @@
|
||||
use crate::consts::constant_simple;
|
||||
use crate::utils;
|
||||
use crate::utils::sugg;
|
||||
use if_chain::if_chain;
|
||||
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::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
@ -10,7 +11,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **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?**
|
||||
/// Concise code helps focusing on behavior instead of boilerplate.
|
||||
@ -33,7 +34,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
pub MANUAL_UNWRAP_OR,
|
||||
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]);
|
||||
@ -43,32 +44,50 @@ impl LateLintPass<'_> for ManualUnwrapOr {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
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>) {
|
||||
fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
|
||||
#[derive(Copy, Clone)]
|
||||
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 arms.len() == 2;
|
||||
if arms.iter().all(|arm| arm.guard.is_none());
|
||||
if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)|
|
||||
if let PatKind::Path(ref qpath) = arm.pat.kind {
|
||||
utils::match_qpath(qpath, &utils::paths::OPTION_NONE)
|
||||
} else {
|
||||
false
|
||||
if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)|
|
||||
match arm.pat.kind {
|
||||
PatKind::Path(ref some_qpath) =>
|
||||
utils::match_qpath(some_qpath, &utils::paths::OPTION_NONE),
|
||||
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];
|
||||
if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind;
|
||||
if utils::match_qpath(some_qpath, &utils::paths::OPTION_SOME);
|
||||
if let PatKind::Binding(_, binding_hir_id, ..) = some_binding.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind;
|
||||
let unwrap_arm = &arms[1 - idx];
|
||||
if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
|
||||
if utils::match_qpath(unwrap_qpath, &utils::paths::OPTION_SOME)
|
||||
|| utils::match_qpath(unwrap_qpath, &utils::paths::RESULT_OK);
|
||||
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 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 {
|
||||
Some(none_arm)
|
||||
Some(or_arm)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -78,24 +97,29 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc
|
||||
if_chain! {
|
||||
if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind;
|
||||
let ty = cx.typeck_results().expr_ty(scrutinee);
|
||||
if utils::is_type_diagnostic_item(cx, ty, sym!(option_type));
|
||||
if let Some(none_arm) = applicable_none_arm(match_arms);
|
||||
if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span);
|
||||
if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span);
|
||||
if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)) {
|
||||
Some(Case::Option)
|
||||
} else if utils::is_type_diagnostic_item(cx, ty, sym!(result_type)) {
|
||||
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 constant_simple(cx, cx.typeck_results(), none_arm.body).is_some();
|
||||
if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
|
||||
then {
|
||||
let reindented_none_body =
|
||||
utils::reindent_multiline(none_body_snippet.into(), true, Some(indent));
|
||||
let reindented_or_body =
|
||||
utils::reindent_multiline(or_body_snippet.into(), true, Some(indent));
|
||||
utils::span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_UNWRAP_OR, expr.span,
|
||||
"this pattern reimplements `Option::unwrap_or`",
|
||||
&format!("this pattern reimplements `{}`", case.unwrap_fn_path()),
|
||||
"replace with",
|
||||
format!(
|
||||
"{}.unwrap_or({})",
|
||||
scrutinee_snippet,
|
||||
reindented_none_body,
|
||||
sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(),
|
||||
reindented_or_body,
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::consts::{constant, miri_to_const, Constant};
|
||||
use crate::utils::paths;
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::usage::is_unused;
|
||||
use crate::utils::{
|
||||
@ -8,8 +7,10 @@ use crate::utils::{
|
||||
snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
|
||||
span_lint_and_then,
|
||||
};
|
||||
use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::CtorKind;
|
||||
use rustc_hir::{
|
||||
@ -18,10 +19,12 @@ use rustc_hir::{
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, Ty, TyS};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::Symbol;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::Bound;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -36,7 +39,6 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # fn bar(stool: &str) {}
|
||||
/// # let x = Some("abc");
|
||||
///
|
||||
/// // Bad
|
||||
/// match x {
|
||||
/// Some(ref foo) => bar(foo),
|
||||
@ -239,7 +241,6 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # enum Foo { A(usize), B(usize) }
|
||||
/// # let x = Foo::B(1);
|
||||
///
|
||||
/// // Bad
|
||||
/// match x {
|
||||
/// Foo::A(_) => {},
|
||||
@ -477,6 +478,47 @@ declare_clippy_lint! {
|
||||
"a match that could be written with the matches! macro"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `match` with identical arm bodies.
|
||||
///
|
||||
/// **Why is this bad?** This is probably a copy & paste error. If arm bodies
|
||||
/// are the same on purpose, you can factor them
|
||||
/// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
|
||||
///
|
||||
/// **Known problems:** False positive possible with order dependent `match`
|
||||
/// (see issue
|
||||
/// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar => bar(),
|
||||
/// Quz => quz(),
|
||||
/// Baz => bar(), // <= oops
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This should probably be
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar => bar(),
|
||||
/// Quz => quz(),
|
||||
/// Baz => baz(), // <= fixed
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// or if the original code was not a typo:
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar | Baz => bar(), // <= shows the intent better
|
||||
/// Quz => quz(),
|
||||
/// }
|
||||
/// ```
|
||||
pub MATCH_SAME_ARMS,
|
||||
pedantic,
|
||||
"`match` with identical arm bodies"
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Matches {
|
||||
infallible_destructuring_match_linted: bool,
|
||||
@ -497,7 +539,8 @@ impl_lint_pass!(Matches => [
|
||||
INFALLIBLE_DESTRUCTURING_MATCH,
|
||||
REST_PAT_IN_FULLY_BOUND_STRUCTS,
|
||||
REDUNDANT_PATTERN_MATCHING,
|
||||
MATCH_LIKE_MATCHES_MACRO
|
||||
MATCH_LIKE_MATCHES_MACRO,
|
||||
MATCH_SAME_ARMS,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
@ -507,7 +550,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
}
|
||||
|
||||
redundant_pattern_match::check(cx, expr);
|
||||
check_match_like_matches(cx, expr);
|
||||
if !check_match_like_matches(cx, expr) {
|
||||
lint_match_arms(cx, expr);
|
||||
}
|
||||
|
||||
if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
|
||||
check_single_match(cx, ex, arms, expr);
|
||||
@ -1065,32 +1110,47 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
|
||||
}
|
||||
|
||||
/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
|
||||
fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind {
|
||||
match match_source {
|
||||
MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false),
|
||||
MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true),
|
||||
_ => return,
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Lint a `match` or desugared `if let` for replacement by `matches!`
|
||||
fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) {
|
||||
fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) -> bool {
|
||||
if_chain! {
|
||||
if arms.len() == 2;
|
||||
if arms.len() >= 2;
|
||||
if cx.typeck_results().expr_ty(expr).is_bool();
|
||||
if is_wild(&arms[1].pat);
|
||||
if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared);
|
||||
if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared);
|
||||
if first != second;
|
||||
if let Some((b1_arm, b0_arms)) = arms.split_last();
|
||||
if let Some(b0) = find_bool_lit(&b0_arms[0].body.kind, desugared);
|
||||
if let Some(b1) = find_bool_lit(&b1_arm.body.kind, desugared);
|
||||
if is_wild(&b1_arm.pat);
|
||||
if b0 != b1;
|
||||
let if_guard = &b0_arms[0].guard;
|
||||
if if_guard.is_none() || b0_arms.len() == 1;
|
||||
if b0_arms[1..].iter()
|
||||
.all(|arm| {
|
||||
find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) &&
|
||||
arm.guard.is_none()
|
||||
});
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard {
|
||||
format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability))
|
||||
let pat = {
|
||||
use itertools::Itertools as _;
|
||||
b0_arms.iter()
|
||||
.map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability))
|
||||
.join(" | ")
|
||||
};
|
||||
let pat_and_guard = if let Some(Guard::If(g)) = if_guard {
|
||||
format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
|
||||
} else {
|
||||
format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability))
|
||||
pat
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
@ -1100,12 +1160,15 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
|
||||
"try this",
|
||||
format!(
|
||||
"{}matches!({}, {})",
|
||||
if first { "" } else { "!" },
|
||||
if b0 { "" } else { "!" },
|
||||
snippet_with_applicability(cx, ex.span, "..", &mut applicability),
|
||||
pat_and_guard,
|
||||
),
|
||||
applicability,
|
||||
)
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1659,3 +1722,119 @@ fn test_overlapping() {
|
||||
],)
|
||||
);
|
||||
}
|
||||
|
||||
/// Implementation of `MATCH_SAME_ARMS`.
|
||||
fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||
fn same_bindings<'tcx>(lhs: &FxHashMap<Symbol, Ty<'tcx>>, rhs: &FxHashMap<Symbol, Ty<'tcx>>) -> bool {
|
||||
lhs.len() == rhs.len()
|
||||
&& lhs
|
||||
.iter()
|
||||
.all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty)))
|
||||
}
|
||||
|
||||
if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
|
||||
let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
|
||||
let mut h = SpanlessHash::new(cx);
|
||||
h.hash_expr(&arm.body);
|
||||
h.finish()
|
||||
};
|
||||
|
||||
let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
|
||||
let min_index = usize::min(lindex, rindex);
|
||||
let max_index = usize::max(lindex, rindex);
|
||||
|
||||
// Arms with a guard are ignored, those can’t always be merged together
|
||||
// This is also the case for arms in-between each there is an arm with a guard
|
||||
(min_index..=max_index).all(|index| arms[index].guard.is_none()) &&
|
||||
SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) &&
|
||||
// all patterns should have the same bindings
|
||||
same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat))
|
||||
};
|
||||
|
||||
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
|
||||
for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MATCH_SAME_ARMS,
|
||||
j.body.span,
|
||||
"this `match` has identical arm bodies",
|
||||
|diag| {
|
||||
diag.span_note(i.body.span, "same as this");
|
||||
|
||||
// Note: this does not use `span_suggestion` on purpose:
|
||||
// there is no clean way
|
||||
// to remove the other arm. Building a span and suggest to replace it to ""
|
||||
// makes an even more confusing error message. Also in order not to make up a
|
||||
// span for the whole pattern, the suggestion is only shown when there is only
|
||||
// one pattern. The user should know about `|` if they are already using it…
|
||||
|
||||
let lhs = snippet(cx, i.pat.span, "<pat1>");
|
||||
let rhs = snippet(cx, j.pat.span, "<pat2>");
|
||||
|
||||
if let PatKind::Wild = j.pat.kind {
|
||||
// if the last arm is _, then i could be integrated into _
|
||||
// note that i.pat cannot be _, because that would mean that we're
|
||||
// hiding all the subsequent arms, and rust won't compile
|
||||
diag.span_note(
|
||||
i.body.span,
|
||||
&format!(
|
||||
"`{}` has the same arm body as the `_` wildcard, consider removing it",
|
||||
lhs
|
||||
),
|
||||
);
|
||||
} else {
|
||||
diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the list of bindings in a pattern.
|
||||
fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap<Symbol, Ty<'tcx>> {
|
||||
fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap<Symbol, Ty<'tcx>>) {
|
||||
match pat.kind {
|
||||
PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
|
||||
PatKind::TupleStruct(_, pats, _) => {
|
||||
for pat in pats {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
},
|
||||
PatKind::Binding(.., ident, ref as_pat) => {
|
||||
if let Entry::Vacant(v) = map.entry(ident.name) {
|
||||
v.insert(cx.typeck_results().pat_ty(pat));
|
||||
}
|
||||
if let Some(ref as_pat) = *as_pat {
|
||||
bindings_impl(cx, as_pat, map);
|
||||
}
|
||||
},
|
||||
PatKind::Or(fields) | PatKind::Tuple(fields, _) => {
|
||||
for pat in fields {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
},
|
||||
PatKind::Struct(_, fields, _) => {
|
||||
for pat in fields {
|
||||
bindings_impl(cx, &pat.pat, map);
|
||||
}
|
||||
},
|
||||
PatKind::Slice(lhs, ref mid, rhs) => {
|
||||
for pat in lhs {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
if let Some(ref mid) = *mid {
|
||||
bindings_impl(cx, mid, map);
|
||||
}
|
||||
for pat in rhs {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
},
|
||||
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (),
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = FxHashMap::default();
|
||||
bindings_impl(cx, pat, &mut result);
|
||||
result
|
||||
}
|
||||
|
@ -231,7 +231,6 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # struct TupleStruct(u32, u32, u32);
|
||||
/// # let t = TupleStruct(1, 2, 3);
|
||||
///
|
||||
/// // Bad
|
||||
/// match t {
|
||||
/// TupleStruct(0, .., _) => (),
|
||||
|
68
clippy_lints/src/mut_mutex_lock.rs
Normal file
68
clippy_lints/src/mut_mutex_lock.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,9 @@
|
||||
//! This lint is **warn** by default
|
||||
|
||||
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 rustc_ast::ast::LitKind;
|
||||
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(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() {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
@ -295,7 +300,14 @@ fn suggest_bool_comparison<'a, 'tcx>(
|
||||
message: &str,
|
||||
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(
|
||||
cx,
|
||||
BOOL_COMPARISON,
|
||||
|
256
clippy_lints/src/pass_by_ref_or_value.rs
Normal file
256
clippy_lints/src/pass_by_ref_or_value.rs
Normal 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));
|
||||
}
|
||||
}
|
@ -2,15 +2,19 @@ use crate::consts::{constant, Constant};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
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_middle::ty;
|
||||
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 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};
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -128,43 +132,51 @@ declare_clippy_lint! {
|
||||
"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 => [
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
RANGE_PLUS_ONE,
|
||||
RANGE_MINUS_ONE,
|
||||
REVERSED_EMPTY_RANGES,
|
||||
MANUAL_RANGE_CONTAINS,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Ranges {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
|
||||
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,
|
||||
expr.span,
|
||||
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
|
||||
snippet(cx, iter_args[0].span, "_")));
|
||||
}
|
||||
}
|
||||
}
|
||||
match expr.kind {
|
||||
ExprKind::MethodCall(ref path, _, ref args, _) => {
|
||||
check_range_zip_with_len(cx, path, args, expr.span);
|
||||
},
|
||||
ExprKind::Binary(ref op, ref l, ref r) => {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr.span);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
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)`
|
||||
fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
@ -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::{
|
||||
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,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
@ -1224,7 +1224,8 @@ 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.
|
||||
///
|
||||
@ -1233,6 +1234,14 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _ = 2i32 as i32;
|
||||
/// let _ = 0.5 as f32;
|
||||
/// ```
|
||||
///
|
||||
/// Better:
|
||||
///
|
||||
/// ```rust
|
||||
/// let _ = 2_i32;
|
||||
/// let _ = 0.5_f32;
|
||||
/// ```
|
||||
pub UNNECESSARY_CAST,
|
||||
complexity,
|
||||
@ -1598,7 +1607,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
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));
|
||||
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 let LitKind::Int(n, _) = lit.node;
|
||||
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);
|
||||
if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal();
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_CAST,
|
||||
expr.span,
|
||||
&format!("casting integer literal to `{}` is unnecessary", cast_to),
|
||||
"try",
|
||||
format!("{}_{}", n, cast_to),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
let literal_str = if is_unary_neg(ex) { format!("-{}", num_lit.integer) } else { num_lit.integer.into() };
|
||||
show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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) => {},
|
||||
_ => {
|
||||
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>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &Expr<'tcx>,
|
||||
|
50
clippy_lints/src/undropped_manually_drops.rs
Normal file
50
clippy_lints/src/undropped_manually_drops.rs
Normal 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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -150,6 +150,8 @@ define_Conf! {
|
||||
(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.
|
||||
(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
|
||||
(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
|
||||
|
@ -261,14 +261,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
||||
pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
|
||||
// The == of idents doesn't work with different contexts,
|
||||
// we have to be explicit about hygiene
|
||||
if left.ident.as_str() != right.ident.as_str() {
|
||||
return false;
|
||||
}
|
||||
match (&left.args, &right.args) {
|
||||
(&None, &None) => true,
|
||||
(&Some(ref l), &Some(ref r)) => self.eq_path_parameters(l, r),
|
||||
_ => false,
|
||||
}
|
||||
left.ident.as_str() == right.ident.as_str()
|
||||
&& both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::consts::{constant_simple, Constant};
|
||||
use crate::utils::{
|
||||
is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints,
|
||||
snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
|
||||
@ -14,9 +15,11 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::symbol::{Symbol, SymbolStr};
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
use std::borrow::{Borrow, Cow};
|
||||
|
||||
@ -229,6 +232,21 @@ declare_clippy_lint! {
|
||||
"using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// Checks the paths module for invalid paths.
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// It indicates a bug in the code.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:** None.
|
||||
pub INVALID_PATHS,
|
||||
internal,
|
||||
"invalid path"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
|
||||
|
||||
impl EarlyLintPass for ClippyLintsInternal {
|
||||
@ -761,3 +779,64 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// This is not a complete resolver for paths. It works on all the paths currently used in the paths
|
||||
// module. That's all it does and all it needs to do.
|
||||
pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
|
||||
if path_to_res(cx, path).is_some() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Some implementations can't be found by `path_to_res`, particularly inherent
|
||||
// implementations of native types. Check lang items.
|
||||
let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
|
||||
let lang_items = cx.tcx.lang_items();
|
||||
for lang_item in lang_items.items() {
|
||||
if let Some(def_id) = lang_item {
|
||||
let lang_item_path = cx.get_def_path(*def_id);
|
||||
if path_syms.starts_with(&lang_item_path) {
|
||||
if let [item] = &path_syms[lang_item_path.len()..] {
|
||||
for child in cx.tcx.item_children(*def_id) {
|
||||
if child.ident.name == *item {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
let local_def_id = &cx.tcx.parent_module(item.hir_id);
|
||||
let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
|
||||
if_chain! {
|
||||
if mod_name.as_str() == "paths";
|
||||
if let hir::ItemKind::Const(ty, body_id) = item.kind;
|
||||
let ty = hir_ty_to_ty(cx.tcx, ty);
|
||||
if let ty::Array(el_ty, _) = &ty.kind();
|
||||
if let ty::Ref(_, el_ty, _) = &el_ty.kind();
|
||||
if el_ty.is_str();
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let typeck_results = cx.tcx.typeck_body(body_id);
|
||||
if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
|
||||
let path: Vec<&str> = path.iter().map(|x| {
|
||||
if let Constant::Str(s) = x {
|
||||
s.as_str()
|
||||
} else {
|
||||
// We checked the type of the constant above
|
||||
unreachable!()
|
||||
}
|
||||
}).collect();
|
||||
if !check_path(cx, &path[..]);
|
||||
then {
|
||||
span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,11 +27,14 @@ pub use self::diagnostics::*;
|
||||
pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::hash::BuildHasherDefault;
|
||||
use std::mem;
|
||||
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{self, Attribute, LitKind};
|
||||
use rustc_attr as attr;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
@ -268,6 +271,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> {
|
||||
krate: *krate,
|
||||
index: CRATE_DEF_INDEX,
|
||||
};
|
||||
let mut current_item = None;
|
||||
let mut items = cx.tcx.item_children(krate);
|
||||
let mut path_it = path.iter().skip(1).peekable();
|
||||
|
||||
@ -277,6 +281,12 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> {
|
||||
None => return None,
|
||||
};
|
||||
|
||||
// `get_def_path` seems to generate these empty segments for extern blocks.
|
||||
// We can just ignore them.
|
||||
if segment.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let result = SmallVec::<[_; 8]>::new();
|
||||
for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() {
|
||||
if item.ident.name.as_str() == *segment {
|
||||
@ -284,10 +294,28 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> {
|
||||
return Some(item.res);
|
||||
}
|
||||
|
||||
current_item = Some(item);
|
||||
items = cx.tcx.item_children(item.res.def_id());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The segment isn't a child_item.
|
||||
// Try to find it under an inherent impl.
|
||||
if_chain! {
|
||||
if path_it.peek().is_none();
|
||||
if let Some(current_item) = current_item;
|
||||
let item_def_id = current_item.res.def_id();
|
||||
if cx.tcx.def_kind(item_def_id) == DefKind::Struct;
|
||||
then {
|
||||
// Bad `find_map` suggestion. See #4193.
|
||||
#[allow(clippy::find_map)]
|
||||
return cx.tcx.inherent_impls(item_def_id).iter()
|
||||
.flat_map(|&impl_def_id| cx.tcx.item_children(impl_def_id))
|
||||
.find(|item| item.ident.name.as_str() == *segment)
|
||||
.map(|item| item.res);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@ -299,7 +327,7 @@ pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) -
|
||||
hir::QPath::Resolved(_, path) => path.res,
|
||||
hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => {
|
||||
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 {
|
||||
Res::Err
|
||||
}
|
||||
@ -1440,6 +1468,41 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<S
|
||||
None
|
||||
}
|
||||
|
||||
/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
|
||||
/// `hash` must be comformed with `eq`
|
||||
pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
|
||||
where
|
||||
Hash: Fn(&T) -> u64,
|
||||
Eq: Fn(&T, &T) -> bool,
|
||||
{
|
||||
if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) {
|
||||
return vec![(&exprs[0], &exprs[1])];
|
||||
}
|
||||
|
||||
let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
|
||||
|
||||
let mut map: FxHashMap<_, Vec<&_>> =
|
||||
FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
|
||||
|
||||
for expr in exprs {
|
||||
match map.entry(hash(expr)) {
|
||||
Entry::Occupied(mut o) => {
|
||||
for o in o.get() {
|
||||
if eq(o, expr) {
|
||||
match_expr_list.push((o, expr));
|
||||
}
|
||||
}
|
||||
o.get_mut().push(expr);
|
||||
},
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(vec![expr]);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
match_expr_list
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unwrap_cargo_metadata {
|
||||
($cx: ident, $lint: ident, $deps: expr) => {{
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
pub enum Radix {
|
||||
Binary,
|
||||
Octal,
|
||||
@ -11,8 +11,8 @@ pub enum Radix {
|
||||
impl Radix {
|
||||
/// Returns a reasonable digit group size for this radix.
|
||||
#[must_use]
|
||||
fn suggest_grouping(&self) -> usize {
|
||||
match *self {
|
||||
fn suggest_grouping(self) -> usize {
|
||||
match self {
|
||||
Self::Binary | Self::Hexadecimal => 4,
|
||||
Self::Octal | Self::Decimal => 3,
|
||||
}
|
||||
|
@ -31,10 +31,10 @@ pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
|
||||
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
|
||||
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
|
||||
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
|
||||
pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
|
||||
pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
|
||||
pub const EXIT: [&str; 3] = ["std", "process", "exit"];
|
||||
pub const F32_EPSILON: [&str; 2] = ["f32", "EPSILON"];
|
||||
pub const F64_EPSILON: [&str; 2] = ["f64", "EPSILON"];
|
||||
pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
|
||||
pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
|
||||
pub const FILE: [&str; 3] = ["std", "fs", "File"];
|
||||
pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
|
||||
pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
|
||||
@ -46,7 +46,7 @@ pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"];
|
||||
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
|
||||
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
|
||||
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
|
||||
pub const HASH: [&str; 2] = ["hash", "Hash"];
|
||||
pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
|
||||
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
|
||||
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
|
||||
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
|
||||
@ -57,7 +57,7 @@ pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "Into
|
||||
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
|
||||
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
|
||||
pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
|
||||
pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"];
|
||||
pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
|
||||
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
|
||||
pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"];
|
||||
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
|
||||
@ -85,13 +85,15 @@ pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
|
||||
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
|
||||
pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
|
||||
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
|
||||
pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
|
||||
pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
|
||||
pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"];
|
||||
pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"];
|
||||
pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
|
||||
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
|
||||
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
|
||||
pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
|
||||
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_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
|
||||
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
|
||||
@ -104,7 +106,7 @@ pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
|
||||
pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
|
||||
pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
|
||||
pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
|
||||
pub const SERDE_DESERIALIZE: [&str; 2] = ["_serde", "Deserialize"];
|
||||
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
|
||||
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
|
||||
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
|
||||
pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
|
||||
|
@ -19,12 +19,11 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) ->
|
||||
used_mutably: FxHashSet::default(),
|
||||
skip: false,
|
||||
};
|
||||
let def_id = expr.hir_id.owner.to_def_id();
|
||||
cx.tcx.infer_ctxt().enter(|infcx| {
|
||||
ExprUseVisitor::new(
|
||||
&mut delegate,
|
||||
&infcx,
|
||||
def_id.expect_local(),
|
||||
expr.hir_id.owner,
|
||||
cx.param_env,
|
||||
cx.typeck_results(),
|
||||
)
|
||||
|
@ -62,10 +62,17 @@ vec![
|
||||
},
|
||||
Lint {
|
||||
name: "await_holding_lock",
|
||||
group: "pedantic",
|
||||
group: "correctness",
|
||||
desc: "Inside an async function, holding a MutexGuard while calling await",
|
||||
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 {
|
||||
name: "bad_bit_mask",
|
||||
@ -1061,6 +1068,13 @@ vec![
|
||||
deprecation: None,
|
||||
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 {
|
||||
name: "len_without_is_empty",
|
||||
group: "style",
|
||||
@ -1159,6 +1173,13 @@ vec![
|
||||
deprecation: None,
|
||||
module: "manual_non_exhaustive",
|
||||
},
|
||||
Lint {
|
||||
name: "manual_range_contains",
|
||||
group: "style",
|
||||
desc: "manually reimplementing {`Range`, `RangeInclusive`}`::contains`",
|
||||
deprecation: None,
|
||||
module: "ranges",
|
||||
},
|
||||
Lint {
|
||||
name: "manual_saturating_arithmetic",
|
||||
group: "style",
|
||||
@ -1183,7 +1204,7 @@ vec![
|
||||
Lint {
|
||||
name: "manual_unwrap_or",
|
||||
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,
|
||||
module: "manual_unwrap_or",
|
||||
},
|
||||
@ -1283,7 +1304,7 @@ vec![
|
||||
group: "pedantic",
|
||||
desc: "`match` with identical arm bodies",
|
||||
deprecation: None,
|
||||
module: "copies",
|
||||
module: "matches",
|
||||
},
|
||||
Lint {
|
||||
name: "match_single_binding",
|
||||
@ -1488,6 +1509,13 @@ vec![
|
||||
deprecation: None,
|
||||
module: "mut_mut",
|
||||
},
|
||||
Lint {
|
||||
name: "mut_mutex_lock",
|
||||
group: "style",
|
||||
desc: "`&mut Mutex::lock` does unnecessary locking",
|
||||
deprecation: None,
|
||||
module: "mut_mutex_lock",
|
||||
},
|
||||
Lint {
|
||||
name: "mut_range_bound",
|
||||
group: "complexity",
|
||||
@ -2125,6 +2153,13 @@ vec![
|
||||
deprecation: None,
|
||||
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 {
|
||||
name: "single_match",
|
||||
group: "style",
|
||||
@ -2382,7 +2417,7 @@ vec![
|
||||
group: "pedantic",
|
||||
desc: "functions taking small copyable arguments by reference",
|
||||
deprecation: None,
|
||||
module: "trivially_copy_pass_by_ref",
|
||||
module: "pass_by_ref_or_value",
|
||||
},
|
||||
Lint {
|
||||
name: "try_err",
|
||||
@ -2405,6 +2440,13 @@ vec![
|
||||
deprecation: None,
|
||||
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 {
|
||||
name: "unicode_not_nfc",
|
||||
group: "pedantic",
|
||||
@ -2594,6 +2636,13 @@ vec![
|
||||
deprecation: None,
|
||||
module: "unused_unit",
|
||||
},
|
||||
Lint {
|
||||
name: "unusual_byte_groupings",
|
||||
group: "style",
|
||||
desc: "binary or hex literals that aren\'t grouped by four",
|
||||
deprecation: None,
|
||||
module: "literal_representation",
|
||||
},
|
||||
Lint {
|
||||
name: "unwrap_in_result",
|
||||
group: "restriction",
|
||||
|
@ -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
|
||||
|
||||
|
86
tests/ui/await_holding_refcell_ref.rs
Normal file
86
tests/ui/await_holding_refcell_ref.rs
Normal 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);
|
||||
}
|
95
tests/ui/await_holding_refcell_ref.stderr
Normal file
95
tests/ui/await_holding_refcell_ref.stderr
Normal 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
|
||||
|
@ -1,6 +1,7 @@
|
||||
// run-rustfix
|
||||
|
||||
#[warn(clippy::bool_comparison)]
|
||||
#![warn(clippy::bool_comparison)]
|
||||
|
||||
fn main() {
|
||||
let x = true;
|
||||
if x {
|
||||
@ -127,3 +128,40 @@ fn issue4983() {
|
||||
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 {}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// run-rustfix
|
||||
|
||||
#[warn(clippy::bool_comparison)]
|
||||
#![warn(clippy::bool_comparison)]
|
||||
|
||||
fn main() {
|
||||
let x = true;
|
||||
if x == true {
|
||||
@ -127,3 +128,40 @@ fn issue4983() {
|
||||
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 {}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: equality checks against true are unnecessary
|
||||
--> $DIR/bool_comparison.rs:6:8
|
||||
--> $DIR/bool_comparison.rs:7:8
|
||||
|
|
||||
LL | if x == true {
|
||||
| ^^^^^^^^^ help: try simplifying it as shown: `x`
|
||||
@ -7,106 +7,130 @@ LL | if x == true {
|
||||
= note: `-D clippy::bool-comparison` implied by `-D warnings`
|
||||
|
||||
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 {
|
||||
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
|
||||
|
||||
error: equality checks against true are unnecessary
|
||||
--> $DIR/bool_comparison.rs:16:8
|
||||
--> $DIR/bool_comparison.rs:17:8
|
||||
|
|
||||
LL | if true == x {
|
||||
| ^^^^^^^^^ help: try simplifying it as shown: `x`
|
||||
|
||||
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 {
|
||||
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
|
||||
|
||||
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 {
|
||||
| ^^^^^^^^^ help: try simplifying it as shown: `!x`
|
||||
|
||||
error: inequality checks against false are unnecessary
|
||||
--> $DIR/bool_comparison.rs:31:8
|
||||
--> $DIR/bool_comparison.rs:32:8
|
||||
|
|
||||
LL | if x != false {
|
||||
| ^^^^^^^^^^ help: try simplifying it as shown: `x`
|
||||
|
||||
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 {
|
||||
| ^^^^^^^^^ help: try simplifying it as shown: `!x`
|
||||
|
||||
error: inequality checks against false are unnecessary
|
||||
--> $DIR/bool_comparison.rs:41:8
|
||||
--> $DIR/bool_comparison.rs:42:8
|
||||
|
|
||||
LL | if false != x {
|
||||
| ^^^^^^^^^^ help: try simplifying it as shown: `x`
|
||||
|
||||
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 {
|
||||
| ^^^^^^^^ help: try simplifying it as shown: `!x`
|
||||
|
||||
error: greater than checks against false are unnecessary
|
||||
--> $DIR/bool_comparison.rs:51:8
|
||||
--> $DIR/bool_comparison.rs:52:8
|
||||
|
|
||||
LL | if false < x {
|
||||
| ^^^^^^^^^ help: try simplifying it as shown: `x`
|
||||
|
||||
error: greater than checks against false are unnecessary
|
||||
--> $DIR/bool_comparison.rs:56:8
|
||||
--> $DIR/bool_comparison.rs:57:8
|
||||
|
|
||||
LL | if x > false {
|
||||
| ^^^^^^^^^ help: try simplifying it as shown: `x`
|
||||
|
||||
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 {
|
||||
| ^^^^^^^^ help: try simplifying it as shown: `!x`
|
||||
|
||||
error: order comparisons between booleans can be simplified
|
||||
--> $DIR/bool_comparison.rs:67:8
|
||||
--> $DIR/bool_comparison.rs:68:8
|
||||
|
|
||||
LL | if x < y {
|
||||
| ^^^^^ help: try simplifying it as shown: `!x & y`
|
||||
|
||||
error: order comparisons between booleans can be simplified
|
||||
--> $DIR/bool_comparison.rs:72:8
|
||||
--> $DIR/bool_comparison.rs:73:8
|
||||
|
|
||||
LL | if x > y {
|
||||
| ^^^^^ help: try simplifying it as shown: `x & !y`
|
||||
|
||||
error: this comparison might be written more concisely
|
||||
--> $DIR/bool_comparison.rs:120:8
|
||||
--> $DIR/bool_comparison.rs:121:8
|
||||
|
|
||||
LL | if a == !b {};
|
||||
| ^^^^^^^ help: try simplifying it as shown: `a != b`
|
||||
|
||||
error: this comparison might be written more concisely
|
||||
--> $DIR/bool_comparison.rs:121:8
|
||||
--> $DIR/bool_comparison.rs:122:8
|
||||
|
|
||||
LL | if !a == b {};
|
||||
| ^^^^^^^ help: try simplifying it as shown: `a != b`
|
||||
|
||||
error: this comparison might be written more concisely
|
||||
--> $DIR/bool_comparison.rs:125:8
|
||||
--> $DIR/bool_comparison.rs:126:8
|
||||
|
|
||||
LL | if b == !a {};
|
||||
| ^^^^^^^ help: try simplifying it as shown: `b != a`
|
||||
|
||||
error: this comparison might be written more concisely
|
||||
--> $DIR/bool_comparison.rs:126:8
|
||||
--> $DIR/bool_comparison.rs:127:8
|
||||
|
|
||||
LL | if !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
|
||||
|
||||
|
@ -124,7 +124,7 @@ error: casting integer literal to `f64` is unnecessary
|
||||
--> $DIR/cast_size_32bit.rs:34:5
|
||||
|
|
||||
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`
|
||||
|
||||
|
@ -12,13 +12,14 @@ LL | | }
|
||||
|
|
||||
= 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
|
||||
|
|
||||
LL | loop {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= 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
|
||||
|
||||
|
@ -9,5 +9,6 @@
|
||||
#[warn(clippy::unused_label)]
|
||||
#[warn(clippy::regex_macro)]
|
||||
#[warn(clippy::drop_bounds)]
|
||||
#[warn(clippy::temporary_cstring_as_ptr)]
|
||||
|
||||
fn main() {}
|
||||
|
@ -66,11 +66,17 @@ error: lint `clippy::drop_bounds` has been removed: `this lint has been uplifted
|
||||
LL | #[warn(clippy::drop_bounds)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr``
|
||||
--> $DIR/deprecated.rs:12:8
|
||||
|
|
||||
LL | #[warn(clippy::temporary_cstring_as_ptr)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
|
||||
--> $DIR/deprecated.rs:1:8
|
||||
|
|
||||
LL | #[warn(clippy::str_to_string)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
|
@ -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
|
||||
|
|
||||
LL | loop {}
|
||||
| ^^^^^^^
|
||||
|
|
||||
= 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
|
||||
|
|
||||
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
|
||||
|
|
||||
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
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
|
||||
#[allow(clippy::nonminimal_bool)]
|
||||
#[allow(unused)]
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn main() {
|
||||
// simple values and comparisons
|
||||
1 == 1;
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: equal expressions as operands to `==`
|
||||
--> $DIR/eq_op.rs:11:5
|
||||
--> $DIR/eq_op.rs:12:5
|
||||
|
|
||||
LL | 1 == 1;
|
||||
| ^^^^^^
|
||||
@ -7,157 +7,157 @@ LL | 1 == 1;
|
||||
= note: `-D clippy::eq-op` implied by `-D warnings`
|
||||
|
||||
error: equal expressions as operands to `==`
|
||||
--> $DIR/eq_op.rs:12:5
|
||||
--> $DIR/eq_op.rs:13:5
|
||||
|
|
||||
LL | "no" == "no";
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `!=`
|
||||
--> $DIR/eq_op.rs:14:5
|
||||
--> $DIR/eq_op.rs:15:5
|
||||
|
|
||||
LL | false != false;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `<`
|
||||
--> $DIR/eq_op.rs:15:5
|
||||
--> $DIR/eq_op.rs:16:5
|
||||
|
|
||||
LL | 1.5 < 1.5;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `>=`
|
||||
--> $DIR/eq_op.rs:16:5
|
||||
--> $DIR/eq_op.rs:17:5
|
||||
|
|
||||
LL | 1u64 >= 1u64;
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `^`
|
||||
--> $DIR/eq_op.rs:20:5
|
||||
--> $DIR/eq_op.rs:21:5
|
||||
|
|
||||
LL | 1 ^ ((((((1))))));
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `<`
|
||||
--> $DIR/eq_op.rs:23:5
|
||||
--> $DIR/eq_op.rs:24:5
|
||||
|
|
||||
LL | (-(2) < -(2));
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
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));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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));
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
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));
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `!=`
|
||||
--> $DIR/eq_op.rs:28:5
|
||||
--> $DIR/eq_op.rs:29:5
|
||||
|
|
||||
LL | ([1] != [1]);
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `!=`
|
||||
--> $DIR/eq_op.rs:29:5
|
||||
--> $DIR/eq_op.rs:30:5
|
||||
|
|
||||
LL | ((1, 2) != (1, 2));
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `==`
|
||||
--> $DIR/eq_op.rs:33:5
|
||||
--> $DIR/eq_op.rs:34:5
|
||||
|
|
||||
LL | 1 + 1 == 2;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `==`
|
||||
--> $DIR/eq_op.rs:34:5
|
||||
--> $DIR/eq_op.rs:35:5
|
||||
|
|
||||
LL | 1 - 1 == 0;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `-`
|
||||
--> $DIR/eq_op.rs:34:5
|
||||
--> $DIR/eq_op.rs:35:5
|
||||
|
|
||||
LL | 1 - 1 == 0;
|
||||
| ^^^^^
|
||||
|
||||
error: equal expressions as operands to `-`
|
||||
--> $DIR/eq_op.rs:36:5
|
||||
--> $DIR/eq_op.rs:37:5
|
||||
|
|
||||
LL | 1 - 1;
|
||||
| ^^^^^
|
||||
|
||||
error: equal expressions as operands to `/`
|
||||
--> $DIR/eq_op.rs:37:5
|
||||
--> $DIR/eq_op.rs:38:5
|
||||
|
|
||||
LL | 1 / 1;
|
||||
| ^^^^^
|
||||
|
||||
error: equal expressions as operands to `&&`
|
||||
--> $DIR/eq_op.rs:38:5
|
||||
--> $DIR/eq_op.rs:39:5
|
||||
|
|
||||
LL | true && true;
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `||`
|
||||
--> $DIR/eq_op.rs:40:5
|
||||
--> $DIR/eq_op.rs:41:5
|
||||
|
|
||||
LL | true || true;
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `&&`
|
||||
--> $DIR/eq_op.rs:46:5
|
||||
--> $DIR/eq_op.rs:47:5
|
||||
|
|
||||
LL | a == b && b == a;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `&&`
|
||||
--> $DIR/eq_op.rs:47:5
|
||||
--> $DIR/eq_op.rs:48:5
|
||||
|
|
||||
LL | a != b && b != a;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `&&`
|
||||
--> $DIR/eq_op.rs:48:5
|
||||
--> $DIR/eq_op.rs:49:5
|
||||
|
|
||||
LL | a < b && b > a;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `&&`
|
||||
--> $DIR/eq_op.rs:49:5
|
||||
--> $DIR/eq_op.rs:50:5
|
||||
|
|
||||
LL | a <= b && b >= a;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: equal expressions as operands to `==`
|
||||
--> $DIR/eq_op.rs:52:5
|
||||
--> $DIR/eq_op.rs:53:5
|
||||
|
|
||||
LL | a == a;
|
||||
| ^^^^^^
|
||||
|
||||
error: equal expressions as operands to `/`
|
||||
--> $DIR/eq_op.rs:62:20
|
||||
--> $DIR/eq_op.rs:63:20
|
||||
|
|
||||
LL | const D: u32 = A / A;
|
||||
| ^^^^^
|
||||
|
@ -3,7 +3,8 @@
|
||||
clippy::blacklisted_name,
|
||||
clippy::collapsible_if,
|
||||
clippy::ifs_same_cond,
|
||||
clippy::needless_return
|
||||
clippy::needless_return,
|
||||
clippy::single_element_loop
|
||||
)]
|
||||
|
||||
fn if_same_then_else2() -> Result<&'static str, ()> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else2.rs:19:12
|
||||
--> $DIR/if_same_then_else2.rs:20:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
@ -13,7 +13,7 @@ LL | | }
|
||||
|
|
||||
= note: `-D clippy::if-same-then-else` implied by `-D warnings`
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else2.rs:10:13
|
||||
--> $DIR/if_same_then_else2.rs:11:13
|
||||
|
|
||||
LL | if true {
|
||||
| _____________^
|
||||
@ -26,7 +26,7 @@ LL | | } else {
|
||||
| |_____^
|
||||
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else2.rs:33:12
|
||||
--> $DIR/if_same_then_else2.rs:34:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
@ -36,7 +36,7 @@ LL | | }
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else2.rs:31:13
|
||||
--> $DIR/if_same_then_else2.rs:32:13
|
||||
|
|
||||
LL | if true {
|
||||
| _____________^
|
||||
@ -45,7 +45,7 @@ LL | | } else {
|
||||
| |_____^
|
||||
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else2.rs:40:12
|
||||
--> $DIR/if_same_then_else2.rs:41:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
@ -55,7 +55,7 @@ LL | | }
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else2.rs:38:13
|
||||
--> $DIR/if_same_then_else2.rs:39:13
|
||||
|
|
||||
LL | if true {
|
||||
| _____________^
|
||||
@ -64,7 +64,7 @@ LL | | } else {
|
||||
| |_____^
|
||||
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else2.rs:90:12
|
||||
--> $DIR/if_same_then_else2.rs:91:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
@ -74,7 +74,7 @@ LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else2.rs:88:21
|
||||
--> $DIR/if_same_then_else2.rs:89:21
|
||||
|
|
||||
LL | let _ = if true {
|
||||
| _____________________^
|
||||
@ -83,7 +83,7 @@ LL | | } else {
|
||||
| |_____^
|
||||
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else2.rs:97:12
|
||||
--> $DIR/if_same_then_else2.rs:98:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
@ -93,7 +93,7 @@ LL | | }
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else2.rs:95:13
|
||||
--> $DIR/if_same_then_else2.rs:96:13
|
||||
|
|
||||
LL | if true {
|
||||
| _____________^
|
||||
@ -102,7 +102,7 @@ LL | | } else {
|
||||
| |_____^
|
||||
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else2.rs:122:12
|
||||
--> $DIR/if_same_then_else2.rs:123:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
@ -112,7 +112,7 @@ LL | | }
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else2.rs:119:20
|
||||
--> $DIR/if_same_then_else2.rs:120:20
|
||||
|
|
||||
LL | } else if true {
|
||||
| ____________________^
|
||||
|
23
tests/ui/invalid_paths.rs
Normal file
23
tests/ui/invalid_paths.rs
Normal file
@ -0,0 +1,23 @@
|
||||
#![warn(clippy::internal)]
|
||||
|
||||
mod paths {
|
||||
// Good path
|
||||
pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
|
||||
|
||||
// Path to method on inherent impl of a primitive type
|
||||
pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
|
||||
|
||||
// Path to method on inherent impl
|
||||
pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
|
||||
|
||||
// Path with empty segment
|
||||
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
|
||||
|
||||
// Path with bad crate
|
||||
pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
|
||||
|
||||
// Path with bad module
|
||||
pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
|
||||
}
|
||||
|
||||
fn main() {}
|
16
tests/ui/invalid_paths.stderr
Normal file
16
tests/ui/invalid_paths.stderr
Normal file
@ -0,0 +1,16 @@
|
||||
error: invalid path
|
||||
--> $DIR/invalid_paths.rs:17:5
|
||||
|
|
||||
LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::clippy-lints-internal` implied by `-D warnings`
|
||||
|
||||
error: invalid path
|
||||
--> $DIR/invalid_paths.rs:20:5
|
||||
|
|
||||
LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -11,7 +11,7 @@ fn main() {
|
||||
let _good = (
|
||||
0b1011_i64,
|
||||
0o1_234_u32,
|
||||
0x1_234_567,
|
||||
0x0123_4567,
|
||||
1_2345_6789,
|
||||
1234_f32,
|
||||
1_234.12_f32,
|
||||
|
@ -1,24 +1,30 @@
|
||||
error: digit groups should be smaller
|
||||
error: digits of hex or binary literal not grouped by four
|
||||
--> $DIR/large_digit_groups.rs:14:9
|
||||
|
|
||||
LL | 0x1_234_567,
|
||||
| ^^^^^^^^^^^ help: consider: `0x0123_4567`
|
||||
|
|
||||
= note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
|
||||
|
||||
error: digits of hex or binary literal not grouped by four
|
||||
--> $DIR/large_digit_groups.rs:22:9
|
||||
|
|
||||
LL | 0b1_10110_i64,
|
||||
| ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64`
|
||||
|
|
||||
= note: `-D clippy::large-digit-groups` implied by `-D warnings`
|
||||
|
||||
error: digits grouped inconsistently by underscores
|
||||
error: digits of hex or binary literal not grouped by four
|
||||
--> $DIR/large_digit_groups.rs:23:9
|
||||
|
|
||||
LL | 0xd_e_adbee_f_usize,
|
||||
| ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize`
|
||||
|
|
||||
= note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
|
||||
|
||||
error: digit groups should be smaller
|
||||
--> $DIR/large_digit_groups.rs:24:9
|
||||
|
|
||||
LL | 1_23456_f32,
|
||||
| ^^^^^^^^^^^ help: consider: `123_456_f32`
|
||||
|
|
||||
= note: `-D clippy::large-digit-groups` implied by `-D warnings`
|
||||
|
||||
error: digit groups should be smaller
|
||||
--> $DIR/large_digit_groups.rs:25:9
|
||||
@ -38,5 +44,5 @@ error: digit groups should be smaller
|
||||
LL | 1_23456.12345_6_f64,
|
||||
| ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
|
66
tests/ui/large_types_passed_by_value.rs
Normal file
66
tests/ui/large_types_passed_by_value.rs
Normal 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() {}
|
52
tests/ui/large_types_passed_by_value.stderr
Normal file
52
tests/ui/large_types_passed_by_value.stderr
Normal 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
|
||||
|
@ -33,4 +33,9 @@ fn main() {
|
||||
let fail19 = 12_3456_21;
|
||||
let fail22 = 3__4___23;
|
||||
let fail23 = 3__16___23;
|
||||
|
||||
let fail24 = 0xAB_ABC_AB;
|
||||
let fail25 = 0b01_100_101;
|
||||
let ok26 = 0x6_A0_BF;
|
||||
let ok27 = 0b1_0010_0101;
|
||||
}
|
||||
|
@ -69,5 +69,19 @@ error: digits grouped inconsistently by underscores
|
||||
LL | let fail23 = 3__16___23;
|
||||
| ^^^^^^^^^^ help: consider: `31_623`
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error: digits of hex or binary literal not grouped by four
|
||||
--> $DIR/literals.rs:37:18
|
||||
|
|
||||
LL | let fail24 = 0xAB_ABC_AB;
|
||||
| ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB`
|
||||
|
|
||||
= note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
|
||||
|
||||
error: digits of hex or binary literal not grouped by four
|
||||
--> $DIR/literals.rs:38:18
|
||||
|
|
||||
LL | let fail25 = 0b01_100_101;
|
||||
| ^^^^^^^^^^^^ help: consider: `0b0110_0101`
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
// run-rustfix
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
fn unwrap_or() {
|
||||
fn option_unwrap_or() {
|
||||
// int case
|
||||
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() {}
|
||||
|
@ -1,7 +1,8 @@
|
||||
// run-rustfix
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
fn unwrap_or() {
|
||||
fn option_unwrap_or() {
|
||||
// int case
|
||||
match Some(1) {
|
||||
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() {}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 | | Some(i) => i,
|
||||
@ -10,7 +10,7 @@ LL | | };
|
||||
= note: `-D clippy::manual-unwrap-or` implied by `-D warnings`
|
||||
|
||||
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 | | None => 42,
|
||||
@ -19,7 +19,7 @@ LL | | };
|
||||
| |_____^ help: replace with: `Some(1).unwrap_or(42)`
|
||||
|
||||
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 | | Some(i) => i,
|
||||
@ -28,7 +28,7 @@ LL | | };
|
||||
| |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)`
|
||||
|
||||
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 | | Some(i) => i,
|
||||
@ -49,7 +49,7 @@ LL | });
|
||||
|
|
||||
|
||||
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 | | Some(i) => i,
|
||||
@ -57,5 +57,89 @@ LL | | None => "Alice",
|
||||
LL | | };
|
||||
| |_____^ 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
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// run-rustfix
|
||||
|
||||
#![warn(clippy::match_like_matches_macro)]
|
||||
#![allow(unreachable_patterns)]
|
||||
#![allow(unreachable_patterns, dead_code)]
|
||||
|
||||
fn main() {
|
||||
let x = Some(5);
|
||||
@ -33,4 +33,70 @@ fn main() {
|
||||
_ => true,
|
||||
None => false,
|
||||
};
|
||||
|
||||
enum E {
|
||||
A(u32),
|
||||
B(i32),
|
||||
C,
|
||||
D,
|
||||
};
|
||||
let x = E::A(2);
|
||||
{
|
||||
// lint
|
||||
let _ans = matches!(x, E::A(_) | E::B(_));
|
||||
}
|
||||
{
|
||||
// lint
|
||||
let _ans = !matches!(x, E::B(_) | E::C);
|
||||
}
|
||||
{
|
||||
// no lint
|
||||
let _ans = match x {
|
||||
E::A(_) => false,
|
||||
E::B(_) => false,
|
||||
E::C => true,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
{
|
||||
// no lint
|
||||
let _ans = match x {
|
||||
E::A(_) => true,
|
||||
E::B(_) => false,
|
||||
E::C => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
{
|
||||
// no lint
|
||||
let _ans = match x {
|
||||
E::A(a) if a < 10 => false,
|
||||
E::B(a) if a < 10 => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
{
|
||||
// no lint
|
||||
let _ans = match x {
|
||||
E::A(_) => false,
|
||||
E::B(a) if a < 10 => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
{
|
||||
// no lint
|
||||
let _ans = match x {
|
||||
E::A(a) => a == 10,
|
||||
E::B(_) => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
{
|
||||
// no lint
|
||||
let _ans = match x {
|
||||
E::A(_) => false,
|
||||
E::B(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// run-rustfix
|
||||
|
||||
#![warn(clippy::match_like_matches_macro)]
|
||||
#![allow(unreachable_patterns)]
|
||||
#![allow(unreachable_patterns, dead_code)]
|
||||
|
||||
fn main() {
|
||||
let x = Some(5);
|
||||
@ -45,4 +45,78 @@ fn main() {
|
||||
_ => true,
|
||||
None => false,
|
||||
};
|
||||
|
||||
enum E {
|
||||
A(u32),
|
||||
B(i32),
|
||||
C,
|
||||
D,
|
||||
};
|
||||
let x = E::A(2);
|
||||
{
|
||||
// lint
|
||||
let _ans = match x {
|
||||
E::A(_) => true,
|
||||
E::B(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
{
|
||||
// lint
|
||||
let _ans = match x {
|
||||
E::B(_) => false,
|
||||
E::C => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
{
|
||||
// no lint
|
||||
let _ans = match x {
|
||||
E::A(_) => false,
|
||||
E::B(_) => false,
|
||||
E::C => true,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
{
|
||||
// no lint
|
||||
let _ans = match x {
|
||||
E::A(_) => true,
|
||||
E::B(_) => false,
|
||||
E::C => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
{
|
||||
// no lint
|
||||
let _ans = match x {
|
||||
E::A(a) if a < 10 => false,
|
||||
E::B(a) if a < 10 => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
{
|
||||
// no lint
|
||||
let _ans = match x {
|
||||
E::A(_) => false,
|
||||
E::B(a) if a < 10 => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
{
|
||||
// no lint
|
||||
let _ans = match x {
|
||||
E::A(a) => a == 10,
|
||||
E::B(_) => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
{
|
||||
// no lint
|
||||
let _ans = match x {
|
||||
E::A(_) => false,
|
||||
E::B(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -48,5 +48,27 @@ error: if let .. else expression looks like `matches!` macro
|
||||
LL | let _zzz = if let Some(5) = x { true } else { false };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))`
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
error: match expression looks like `matches!` macro
|
||||
--> $DIR/match_expr_like_matches_macro.rs:58:20
|
||||
|
|
||||
LL | let _ans = match x {
|
||||
| ____________________^
|
||||
LL | | E::A(_) => true,
|
||||
LL | | E::B(_) => true,
|
||||
LL | | _ => false,
|
||||
LL | | };
|
||||
| |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
|
||||
|
||||
error: match expression looks like `matches!` macro
|
||||
--> $DIR/match_expr_like_matches_macro.rs:66:20
|
||||
|
|
||||
LL | let _ans = match x {
|
||||
| ____________________^
|
||||
LL | | E::B(_) => false,
|
||||
LL | | E::C => false,
|
||||
LL | | _ => true,
|
||||
LL | | };
|
||||
| |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
|
@ -119,6 +119,22 @@ fn match_same_arms() {
|
||||
unreachable!();
|
||||
},
|
||||
}
|
||||
|
||||
match_expr_like_matches_macro_priority();
|
||||
}
|
||||
|
||||
fn match_expr_like_matches_macro_priority() {
|
||||
enum E {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
}
|
||||
let x = E::A;
|
||||
let _ans = match x {
|
||||
E::A => false,
|
||||
E::B => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -141,5 +141,18 @@ LL | Ok(3) => println!("ok"),
|
||||
| ^^^^^
|
||||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
error: match expression looks like `matches!` macro
|
||||
--> $DIR/match_same_arms2.rs:133:16
|
||||
|
|
||||
LL | let _ans = match x {
|
||||
| ________________^
|
||||
LL | | E::A => false,
|
||||
LL | | E::B => false,
|
||||
LL | | _ => true,
|
||||
LL | | };
|
||||
| |_____^ help: try this: `!matches!(x, E::A | E::B)`
|
||||
|
|
||||
= note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
|
21
tests/ui/mut_mutex_lock.fixed
Normal file
21
tests/ui/mut_mutex_lock.fixed
Normal 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() {}
|
21
tests/ui/mut_mutex_lock.rs
Normal file
21
tests/ui/mut_mutex_lock.rs
Normal 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() {}
|
10
tests/ui/mut_mutex_lock.stderr
Normal file
10
tests/ui/mut_mutex_lock.stderr
Normal 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
|
||||
|
@ -16,4 +16,10 @@ fn main() {
|
||||
.into_iter()
|
||||
.map(|x| (*x, *x + 1))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
// #6202
|
||||
let a = "a".to_string();
|
||||
let sample = vec![a.clone(), "b".to_string(), "c".to_string()];
|
||||
let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
|
||||
non_copy_contains.contains(&a);
|
||||
}
|
||||
|
@ -48,8 +48,21 @@ LL | | indirect_contains.contains(&&5);
|
||||
help: Check if the original Iterator contains an element instead of collecting then checking
|
||||
|
|
||||
LL |
|
||||
LL | sample.iter().any(|x| x == &&5);
|
||||
LL | sample.iter().any(|x| x == &5);
|
||||
|
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: avoid using `collect()` when not needed
|
||||
--> $DIR/needless_collect_indirect.rs:23:5
|
||||
|
|
||||
LL | / let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
|
||||
LL | | non_copy_contains.contains(&a);
|
||||
| |____^
|
||||
|
|
||||
help: Check if the original Iterator contains an element instead of collecting then checking
|
||||
|
|
||||
LL |
|
||||
LL | sample.into_iter().any(|x| x == a);
|
||||
|
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
|
@ -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() {}
|
||||
|
41
tests/ui/range_contains.fixed
Normal file
41
tests/ui/range_contains.fixed
Normal 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;
|
||||
}
|
41
tests/ui/range_contains.rs
Normal file
41
tests/ui/range_contains.rs
Normal 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;
|
||||
}
|
76
tests/ui/range_contains.stderr
Normal file
76
tests/ui/range_contains.stderr
Normal 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
|
||||
|
11
tests/ui/single_element_loop.fixed
Normal file
11
tests/ui/single_element_loop.fixed
Normal 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);
|
||||
}
|
||||
}
|
10
tests/ui/single_element_loop.rs
Normal file
10
tests/ui/single_element_loop.rs
Normal 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);
|
||||
}
|
||||
}
|
19
tests/ui/single_element_loop.stderr
Normal file
19
tests/ui/single_element_loop.stderr
Normal 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
|
||||
|
26
tests/ui/undropped_manually_drops.rs
Normal file
26
tests/ui/undropped_manually_drops.rs
Normal 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);
|
||||
}
|
||||
}
|
19
tests/ui/undropped_manually_drops.stderr
Normal file
19
tests/ui/undropped_manually_drops.stderr
Normal 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
|
||||
|
@ -8,16 +8,31 @@ fn main() {
|
||||
100_f32;
|
||||
100_f64;
|
||||
100_f64;
|
||||
let _ = -100_f32;
|
||||
let _ = -100_f64;
|
||||
let _ = -100_f64;
|
||||
// Should not trigger
|
||||
#[rustfmt::skip]
|
||||
let v = vec!(1);
|
||||
&v as &[i32];
|
||||
1.0 as f64;
|
||||
1 as u64;
|
||||
0x10 as f32;
|
||||
0o10 as f32;
|
||||
0b10 as f32;
|
||||
0x11 as f64;
|
||||
0o11 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;
|
||||
}
|
||||
|
@ -8,16 +8,31 @@ fn main() {
|
||||
100 as f32;
|
||||
100 as f64;
|
||||
100_i32 as f64;
|
||||
let _ = -100 as f32;
|
||||
let _ = -100 as f64;
|
||||
let _ = -100_i32 as f64;
|
||||
// Should not trigger
|
||||
#[rustfmt::skip]
|
||||
let v = vec!(1);
|
||||
&v as &[i32];
|
||||
1.0 as f64;
|
||||
1 as u64;
|
||||
0x10 as f32;
|
||||
0o10 as f32;
|
||||
0b10 as f32;
|
||||
0x11 as f64;
|
||||
0o11 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;
|
||||
}
|
||||
|
@ -18,5 +18,77 @@ error: casting integer literal to `f64` is unnecessary
|
||||
LL | 100_i32 as 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
|
||||
|
||||
|
@ -14,7 +14,7 @@ fn main() {
|
||||
let _good = (
|
||||
0b1011_i64,
|
||||
0o1_234_u32,
|
||||
0x1_234_567,
|
||||
0x0123_4567,
|
||||
65536,
|
||||
1_2345_6789,
|
||||
1234_f32,
|
||||
|
@ -1,3 +1,11 @@
|
||||
error: digits of hex or binary literal not grouped by four
|
||||
--> $DIR/unreadable_literal.rs:17:9
|
||||
|
|
||||
LL | 0x1_234_567,
|
||||
| ^^^^^^^^^^^ help: consider: `0x0123_4567`
|
||||
|
|
||||
= note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
|
||||
|
||||
error: long literal lacking separators
|
||||
--> $DIR/unreadable_literal.rs:25:17
|
||||
|
|
||||
@ -54,5 +62,5 @@ error: long literal lacking separators
|
||||
LL | let _fail12: i128 = 0xabcabcabcabcabcabc;
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc`
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
[relabel]
|
||||
allow-unauthenticated = [
|
||||
"C-*", "A-*", "E-*", "L-*", "M-*", "O-*", "S-*",
|
||||
"good first issue", "needs test"
|
||||
"A-*", "C-*", "E-*", "L-*", "M-*", "O-*", "P-*", "S-*", "T-*",
|
||||
"good first issue"
|
||||
]
|
||||
|
||||
[assign]
|
||||
|
Loading…
Reference in New Issue
Block a user