mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 07:04:18 +00:00
Merge commit 'd0cf3481a84e3aa68c2f185c460e282af36ebc42' into clippyup
This commit is contained in:
parent
1f069c0ce7
commit
1fa3d66e62
63 changed files with 1973 additions and 562 deletions
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -18,7 +18,7 @@ body:
|
|||
id: reproducer
|
||||
attributes:
|
||||
label: Reproducer
|
||||
description: Please provide the code and steps to repoduce the bug
|
||||
description: Please provide the code and steps to reproduce the bug
|
||||
value: |
|
||||
I tried this code:
|
||||
|
||||
|
|
|
@ -3069,6 +3069,7 @@ Released 2018-09-13
|
|||
[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
|
||||
[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
|
||||
[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
|
||||
[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
|
||||
[`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
|
||||
[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
|
||||
[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
|
||||
|
@ -3366,6 +3367,7 @@ Released 2018-09-13
|
|||
[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
|
||||
[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
|
||||
[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
|
||||
[`or_then_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_then_unwrap
|
||||
[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
|
||||
[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
|
||||
[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
|
||||
|
@ -3506,6 +3508,7 @@ Released 2018-09-13
|
|||
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
|
||||
[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
|
||||
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
|
||||
[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
|
||||
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
|
||||
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
|
||||
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
|
||||
|
|
21
clippy_lints/src/casts/cast_enum_constructor.rs
Normal file
21
clippy_lints/src/casts/cast_enum_constructor.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
use super::CAST_ENUM_CONSTRUCTOR;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>) {
|
||||
if matches!(cast_from.kind(), ty::FnDef(..))
|
||||
&& let ExprKind::Path(path) = &cast_expr.kind
|
||||
&& let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = cx.qpath_res(path, cast_expr.hir_id)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
CAST_ENUM_CONSTRUCTOR,
|
||||
expr.span,
|
||||
"cast of an enum tuple constructor to an integer",
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
mod cast_enum_constructor;
|
||||
mod cast_lossless;
|
||||
mod cast_possible_truncation;
|
||||
mod cast_possible_wrap;
|
||||
|
@ -454,6 +455,24 @@ declare_clippy_lint! {
|
|||
"casting using `as` between raw pointers to slices of types with different sizes"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from an enum tuple constructor to an integer.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The cast is easily confused with casting a c-like enum value to an integer.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// enum E { X(i32) };
|
||||
/// let _ = E::X as usize;
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_ENUM_CONSTRUCTOR,
|
||||
suspicious,
|
||||
"casts from an enum tuple constructor to an integer"
|
||||
}
|
||||
|
||||
pub struct Casts {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
@ -481,6 +500,7 @@ impl_lint_pass!(Casts => [
|
|||
CHAR_LIT_AS_U8,
|
||||
PTR_AS_PTR,
|
||||
CAST_ENUM_TRUNCATION,
|
||||
CAST_ENUM_CONSTRUCTOR
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
|
@ -518,6 +538,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
}
|
||||
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
|
||||
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// Should be written:
|
||||
///
|
||||
/// ```rust.ignore
|
||||
/// ```rust,ignore
|
||||
/// if x && y {
|
||||
/// …
|
||||
/// }
|
||||
|
@ -76,7 +76,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// Should be written:
|
||||
///
|
||||
/// ```rust.ignore
|
||||
/// ```rust,ignore
|
||||
/// if x {
|
||||
/// …
|
||||
/// } else if y {
|
||||
|
|
|
@ -637,12 +637,6 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
|
|||
loop {
|
||||
match parser.parse_item(ForceCollect::No) {
|
||||
Ok(Some(item)) => match &item.kind {
|
||||
// Tests with one of these items are ignored
|
||||
ItemKind::Static(..)
|
||||
| ItemKind::Const(..)
|
||||
| ItemKind::ExternCrate(..)
|
||||
| ItemKind::ForeignMod(..) => return false,
|
||||
// We found a main function ...
|
||||
ItemKind::Fn(box Fn {
|
||||
sig, body: Some(block), ..
|
||||
}) if item.ident.name == sym::main => {
|
||||
|
@ -661,8 +655,13 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
|
|||
return false;
|
||||
}
|
||||
},
|
||||
// Another function was found; this case is ignored too
|
||||
ItemKind::Fn(..) => return false,
|
||||
// Tests with one of these items are ignored
|
||||
ItemKind::Static(..)
|
||||
| ItemKind::Const(..)
|
||||
| ItemKind::ExternCrate(..)
|
||||
| ItemKind::ForeignMod(..)
|
||||
// Another function was found; this case is ignored
|
||||
| ItemKind::Fn(..) => return false,
|
||||
_ => {},
|
||||
},
|
||||
Ok(None) => break,
|
||||
|
|
|
@ -23,6 +23,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||
LintId::of(booleans::LOGIC_BUG),
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
|
||||
LintId::of(casts::CAST_ENUM_TRUNCATION),
|
||||
LintId::of(casts::CAST_REF_TO_MUT),
|
||||
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
|
||||
|
@ -166,7 +167,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(methods::ITER_NTH_ZERO),
|
||||
LintId::of(methods::ITER_OVEREAGER_CLONED),
|
||||
LintId::of(methods::ITER_SKIP_NEXT),
|
||||
LintId::of(methods::ITER_WITH_DRAIN),
|
||||
LintId::of(methods::MANUAL_FILTER_MAP),
|
||||
LintId::of(methods::MANUAL_FIND_MAP),
|
||||
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
|
@ -182,6 +182,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(methods::OPTION_FILTER_MAP),
|
||||
LintId::of(methods::OPTION_MAP_OR_NONE),
|
||||
LintId::of(methods::OR_FUN_CALL),
|
||||
LintId::of(methods::OR_THEN_UNWRAP),
|
||||
LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
|
||||
LintId::of(methods::SEARCH_IS_SOME),
|
||||
LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
|
||||
|
@ -290,7 +291,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||
LintId::of(transmute::WRONG_TRANSMUTE),
|
||||
LintId::of(transmuting_null::TRANSMUTING_NULL),
|
||||
LintId::of(try_err::TRY_ERR),
|
||||
LintId::of(types::BORROWED_BOX),
|
||||
LintId::of(types::BOX_COLLECTION),
|
||||
LintId::of(types::REDUNDANT_ALLOCATION),
|
||||
|
|
|
@ -47,6 +47,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(methods::NEEDLESS_SPLITN),
|
||||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||
LintId::of(methods::OPTION_FILTER_MAP),
|
||||
LintId::of(methods::OR_THEN_UNWRAP),
|
||||
LintId::of(methods::SEARCH_IS_SOME),
|
||||
LintId::of(methods::SKIP_WHILE_NEXT),
|
||||
LintId::of(methods::UNNECESSARY_FILTER_MAP),
|
||||
|
|
|
@ -70,6 +70,7 @@ store.register_lints(&[
|
|||
cargo::REDUNDANT_FEATURE_NAMES,
|
||||
cargo::WILDCARD_DEPENDENCIES,
|
||||
case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
casts::CAST_ENUM_CONSTRUCTOR,
|
||||
casts::CAST_ENUM_TRUNCATION,
|
||||
casts::CAST_LOSSLESS,
|
||||
casts::CAST_POSSIBLE_TRUNCATION,
|
||||
|
@ -319,6 +320,7 @@ store.register_lints(&[
|
|||
methods::OPTION_FILTER_MAP,
|
||||
methods::OPTION_MAP_OR_NONE,
|
||||
methods::OR_FUN_CALL,
|
||||
methods::OR_THEN_UNWRAP,
|
||||
methods::RESULT_MAP_OR_INTO_OPTION,
|
||||
methods::SEARCH_IS_SOME,
|
||||
methods::SHOULD_IMPLEMENT_TRAIT,
|
||||
|
@ -332,6 +334,7 @@ store.register_lints(&[
|
|||
methods::UNNECESSARY_FILTER_MAP,
|
||||
methods::UNNECESSARY_FIND_MAP,
|
||||
methods::UNNECESSARY_FOLD,
|
||||
methods::UNNECESSARY_JOIN,
|
||||
methods::UNNECESSARY_LAZY_EVALUATIONS,
|
||||
methods::UNNECESSARY_TO_OWNED,
|
||||
methods::UNWRAP_OR_ELSE_DEFAULT,
|
||||
|
|
|
@ -13,6 +13,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
|||
LintId::of(future_not_send::FUTURE_NOT_SEND),
|
||||
LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
|
||||
LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
|
||||
LintId::of(methods::ITER_WITH_DRAIN),
|
||||
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
|
||||
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
|
||||
LintId::of(mutex_atomic::MUTEX_ATOMIC),
|
||||
|
|
|
@ -63,6 +63,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
|||
LintId::of(methods::IMPLICIT_CLONE),
|
||||
LintId::of(methods::INEFFICIENT_TO_STRING),
|
||||
LintId::of(methods::MAP_UNWRAP_OR),
|
||||
LintId::of(methods::UNNECESSARY_JOIN),
|
||||
LintId::of(misc::FLOAT_CMP),
|
||||
LintId::of(misc::USED_UNDERSCORE_BINDING),
|
||||
LintId::of(mut_mut::MUT_MUT),
|
||||
|
|
|
@ -16,7 +16,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
|
|||
LintId::of(methods::EXTEND_WITH_DRAIN),
|
||||
LintId::of(methods::ITER_NTH),
|
||||
LintId::of(methods::ITER_OVEREAGER_CLONED),
|
||||
LintId::of(methods::ITER_WITH_DRAIN),
|
||||
LintId::of(methods::MANUAL_STR_REPEAT),
|
||||
LintId::of(methods::OR_FUN_CALL),
|
||||
LintId::of(methods::SINGLE_CHAR_PATTERN),
|
||||
|
|
|
@ -62,6 +62,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
|||
LintId::of(strings::STRING_SLICE),
|
||||
LintId::of(strings::STRING_TO_STRING),
|
||||
LintId::of(strings::STR_TO_STRING),
|
||||
LintId::of(try_err::TRY_ERR),
|
||||
LintId::of(types::RC_BUFFER),
|
||||
LintId::of(types::RC_MUTEX),
|
||||
LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS),
|
||||
|
|
|
@ -105,7 +105,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
||||
LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
|
||||
LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
|
||||
LintId::of(try_err::TRY_ERR),
|
||||
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
|
||||
LintId::of(unused_unit::UNUSED_UNIT),
|
||||
LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
|
||||
|
|
|
@ -7,6 +7,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
|
|||
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
|
||||
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
|
||||
LintId::of(casts::CAST_ENUM_TRUNCATION),
|
||||
LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
|
||||
LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
// FIXME: switch to something more ergonomic here, once available.
|
||||
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
|
||||
extern crate rustc_arena;
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_ast_pretty;
|
||||
extern crate rustc_attr;
|
||||
|
|
|
@ -1,19 +1,66 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{path_to_local, search_same, SpanlessEq, SpanlessHash};
|
||||
use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdSet, Pat, PatKind};
|
||||
use core::cmp::Ordering;
|
||||
use core::iter;
|
||||
use core::slice;
|
||||
use rustc_arena::DroplessArena;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdSet, Pat, PatKind, RangeEnd};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::Symbol;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
use super::MATCH_SAME_ARMS;
|
||||
|
||||
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
||||
let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
|
||||
let mut h = SpanlessHash::new(cx);
|
||||
h.hash_expr(arm.body);
|
||||
h.finish()
|
||||
};
|
||||
|
||||
let arena = DroplessArena::default();
|
||||
let normalized_pats: Vec<_> = arms
|
||||
.iter()
|
||||
.map(|a| NormalizedPat::from_pat(cx, &arena, a.pat))
|
||||
.collect();
|
||||
|
||||
// The furthast forwards a pattern can move without semantic changes
|
||||
let forwards_blocking_idxs: Vec<_> = normalized_pats
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, pat)| {
|
||||
normalized_pats[i + 1..]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(j, other)| pat.has_overlapping_values(other).then(|| i + 1 + j))
|
||||
.unwrap_or(normalized_pats.len())
|
||||
})
|
||||
.collect();
|
||||
|
||||
// The furthast backwards a pattern can move without semantic changes
|
||||
let backwards_blocking_idxs: Vec<_> = normalized_pats
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, pat)| {
|
||||
normalized_pats[..i]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.zip(forwards_blocking_idxs[..i].iter().copied().rev())
|
||||
.skip_while(|&(_, forward_block)| forward_block > i)
|
||||
.find_map(|((j, other), forward_block)| {
|
||||
(forward_block == i || pat.has_overlapping_values(other)).then(|| j)
|
||||
})
|
||||
.unwrap_or(0)
|
||||
})
|
||||
.collect();
|
||||
|
||||
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);
|
||||
|
@ -42,53 +89,316 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
|||
}
|
||||
};
|
||||
// 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)
|
||||
.expr_fallback(eq_fallback)
|
||||
.eq_expr(lhs.body, rhs.body)
|
||||
// these checks could be removed to allow unused bindings
|
||||
&& bindings_eq(lhs.pat, local_map.keys().copied().collect())
|
||||
&& bindings_eq(rhs.pat, local_map.values().copied().collect())
|
||||
// If both arms overlap with an arm in between then these can't be merged either.
|
||||
!(backwards_blocking_idxs[max_index] > min_index && forwards_blocking_idxs[min_index] < max_index)
|
||||
&& lhs.guard.is_none()
|
||||
&& rhs.guard.is_none()
|
||||
&& SpanlessEq::new(cx)
|
||||
.expr_fallback(eq_fallback)
|
||||
.eq_expr(lhs.body, rhs.body)
|
||||
// these checks could be removed to allow unused bindings
|
||||
&& bindings_eq(lhs.pat, local_map.keys().copied().collect())
|
||||
&& bindings_eq(rhs.pat, local_map.values().copied().collect())
|
||||
};
|
||||
|
||||
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");
|
||||
for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
|
||||
if matches!(arm2.pat.kind, PatKind::Wild) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MATCH_SAME_ARMS,
|
||||
arm1.span,
|
||||
"this match arm has an identical body to the `_` wildcard arm",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
arm1.span,
|
||||
"try removing the arm",
|
||||
String::new(),
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.help("or try changing either arm body")
|
||||
.span_note(arm2.span, "`_` wildcard arm here");
|
||||
},
|
||||
);
|
||||
} else {
|
||||
let back_block = backwards_blocking_idxs[j];
|
||||
let (keep_arm, move_arm) = if back_block < i || (back_block == 0 && forwards_blocking_idxs[i] <= j) {
|
||||
(arm1, arm2)
|
||||
} else {
|
||||
(arm2, arm1)
|
||||
};
|
||||
|
||||
// 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…
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MATCH_SAME_ARMS,
|
||||
keep_arm.span,
|
||||
"this match arm has an identical body to another arm",
|
||||
|diag| {
|
||||
let move_pat_snip = snippet(cx, move_arm.pat.span, "<pat2>");
|
||||
let keep_pat_snip = snippet(cx, keep_arm.pat.span, "<pat1>");
|
||||
|
||||
let lhs = snippet(cx, i.pat.span, "<pat1>");
|
||||
let rhs = snippet(cx, j.pat.span, "<pat2>");
|
||||
diag.span_suggestion(
|
||||
keep_arm.pat.span,
|
||||
"try merging the arm patterns",
|
||||
format!("{} | {}", keep_pat_snip, move_pat_snip),
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
.help("or try changing either arm body")
|
||||
.span_note(move_arm.span, "other arm here");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,))
|
||||
.help("...or consider changing the match arm bodies");
|
||||
#[derive(Clone, Copy)]
|
||||
enum NormalizedPat<'a> {
|
||||
Wild,
|
||||
Struct(Option<DefId>, &'a [(Symbol, Self)]),
|
||||
Tuple(Option<DefId>, &'a [Self]),
|
||||
Or(&'a [Self]),
|
||||
Path(Option<DefId>),
|
||||
LitStr(Symbol),
|
||||
LitBytes(&'a [u8]),
|
||||
LitInt(u128),
|
||||
LitBool(bool),
|
||||
Range(PatRange),
|
||||
/// A slice pattern. If the second value is `None`, then this matches an exact size. Otherwise
|
||||
/// the first value contains everything before the `..` wildcard pattern, and the second value
|
||||
/// contains everything afterwards. Note that either side, or both sides, may contain zero
|
||||
/// patterns.
|
||||
Slice(&'a [Self], Option<&'a [Self]>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct PatRange {
|
||||
start: u128,
|
||||
end: u128,
|
||||
bounds: RangeEnd,
|
||||
}
|
||||
impl PatRange {
|
||||
fn contains(&self, x: u128) -> bool {
|
||||
x >= self.start
|
||||
&& match self.bounds {
|
||||
RangeEnd::Included => x <= self.end,
|
||||
RangeEnd::Excluded => x < self.end,
|
||||
}
|
||||
}
|
||||
|
||||
fn overlaps(&self, other: &Self) -> bool {
|
||||
// Note: Empty ranges are impossible, so this is correct even though it would return true if an
|
||||
// empty exclusive range were to reside within an inclusive range.
|
||||
(match self.bounds {
|
||||
RangeEnd::Included => self.end >= other.start,
|
||||
RangeEnd::Excluded => self.end > other.start,
|
||||
} && match other.bounds {
|
||||
RangeEnd::Included => self.start <= other.end,
|
||||
RangeEnd::Excluded => self.start < other.end,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates over the pairs of fields with matching names.
|
||||
fn iter_matching_struct_fields<'a>(
|
||||
left: &'a [(Symbol, NormalizedPat<'a>)],
|
||||
right: &'a [(Symbol, NormalizedPat<'a>)],
|
||||
) -> impl Iterator<Item = (&'a NormalizedPat<'a>, &'a NormalizedPat<'a>)> + 'a {
|
||||
struct Iter<'a>(
|
||||
slice::Iter<'a, (Symbol, NormalizedPat<'a>)>,
|
||||
slice::Iter<'a, (Symbol, NormalizedPat<'a>)>,
|
||||
);
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = (&'a NormalizedPat<'a>, &'a NormalizedPat<'a>);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// Note: all the fields in each slice are sorted by symbol value.
|
||||
let mut left = self.0.next()?;
|
||||
let mut right = self.1.next()?;
|
||||
loop {
|
||||
match left.0.cmp(&right.0) {
|
||||
Ordering::Equal => return Some((&left.1, &right.1)),
|
||||
Ordering::Less => left = self.0.next()?,
|
||||
Ordering::Greater => right = self.1.next()?,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Iter(left.iter(), right.iter())
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names)]
|
||||
impl<'a> NormalizedPat<'a> {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self {
|
||||
match pat.kind {
|
||||
PatKind::Wild | PatKind::Binding(.., None) => Self::Wild,
|
||||
PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Ref(pat, _) => {
|
||||
Self::from_pat(cx, arena, pat)
|
||||
},
|
||||
);
|
||||
PatKind::Struct(ref path, fields, _) => {
|
||||
let fields =
|
||||
arena.alloc_from_iter(fields.iter().map(|f| (f.ident.name, Self::from_pat(cx, arena, f.pat))));
|
||||
fields.sort_by_key(|&(name, _)| name);
|
||||
Self::Struct(cx.qpath_res(path, pat.hir_id).opt_def_id(), fields)
|
||||
},
|
||||
PatKind::TupleStruct(ref path, pats, wild_idx) => {
|
||||
let adt = match cx.typeck_results().pat_ty(pat).ty_adt_def() {
|
||||
Some(x) => x,
|
||||
None => return Self::Wild,
|
||||
};
|
||||
let (var_id, variant) = if adt.is_enum() {
|
||||
match cx.qpath_res(path, pat.hir_id).opt_def_id() {
|
||||
Some(x) => (Some(x), adt.variant_with_ctor_id(x)),
|
||||
None => return Self::Wild,
|
||||
}
|
||||
} else {
|
||||
(None, adt.non_enum_variant())
|
||||
};
|
||||
let (front, back) = match wild_idx {
|
||||
Some(i) => pats.split_at(i),
|
||||
None => (pats, [].as_slice()),
|
||||
};
|
||||
let pats = arena.alloc_from_iter(
|
||||
front
|
||||
.iter()
|
||||
.map(|pat| Self::from_pat(cx, arena, pat))
|
||||
.chain(iter::repeat_with(|| Self::Wild).take(variant.fields.len() - pats.len()))
|
||||
.chain(back.iter().map(|pat| Self::from_pat(cx, arena, pat))),
|
||||
);
|
||||
Self::Tuple(var_id, pats)
|
||||
},
|
||||
PatKind::Or(pats) => Self::Or(arena.alloc_from_iter(pats.iter().map(|pat| Self::from_pat(cx, arena, pat)))),
|
||||
PatKind::Path(ref path) => Self::Path(cx.qpath_res(path, pat.hir_id).opt_def_id()),
|
||||
PatKind::Tuple(pats, wild_idx) => {
|
||||
let field_count = match cx.typeck_results().pat_ty(pat).kind() {
|
||||
ty::Tuple(subs) => subs.len(),
|
||||
_ => return Self::Wild,
|
||||
};
|
||||
let (front, back) = match wild_idx {
|
||||
Some(i) => pats.split_at(i),
|
||||
None => (pats, [].as_slice()),
|
||||
};
|
||||
let pats = arena.alloc_from_iter(
|
||||
front
|
||||
.iter()
|
||||
.map(|pat| Self::from_pat(cx, arena, pat))
|
||||
.chain(iter::repeat_with(|| Self::Wild).take(field_count - pats.len()))
|
||||
.chain(back.iter().map(|pat| Self::from_pat(cx, arena, pat))),
|
||||
);
|
||||
Self::Tuple(None, pats)
|
||||
},
|
||||
PatKind::Lit(e) => match &e.kind {
|
||||
// TODO: Handle negative integers. They're currently treated as a wild match.
|
||||
ExprKind::Lit(lit) => match lit.node {
|
||||
LitKind::Str(sym, _) => Self::LitStr(sym),
|
||||
LitKind::ByteStr(ref bytes) => Self::LitBytes(&**bytes),
|
||||
LitKind::Byte(val) => Self::LitInt(val.into()),
|
||||
LitKind::Char(val) => Self::LitInt(val.into()),
|
||||
LitKind::Int(val, _) => Self::LitInt(val),
|
||||
LitKind::Bool(val) => Self::LitBool(val),
|
||||
LitKind::Float(..) | LitKind::Err(_) => Self::Wild,
|
||||
},
|
||||
_ => Self::Wild,
|
||||
},
|
||||
PatKind::Range(start, end, bounds) => {
|
||||
// TODO: Handle negative integers. They're currently treated as a wild match.
|
||||
let start = match start {
|
||||
None => 0,
|
||||
Some(e) => match &e.kind {
|
||||
ExprKind::Lit(lit) => match lit.node {
|
||||
LitKind::Int(val, _) => val,
|
||||
LitKind::Char(val) => val.into(),
|
||||
LitKind::Byte(val) => val.into(),
|
||||
_ => return Self::Wild,
|
||||
},
|
||||
_ => return Self::Wild,
|
||||
},
|
||||
};
|
||||
let (end, bounds) = match end {
|
||||
None => (u128::MAX, RangeEnd::Included),
|
||||
Some(e) => match &e.kind {
|
||||
ExprKind::Lit(lit) => match lit.node {
|
||||
LitKind::Int(val, _) => (val, bounds),
|
||||
LitKind::Char(val) => (val.into(), bounds),
|
||||
LitKind::Byte(val) => (val.into(), bounds),
|
||||
_ => return Self::Wild,
|
||||
},
|
||||
_ => return Self::Wild,
|
||||
},
|
||||
};
|
||||
Self::Range(PatRange { start, end, bounds })
|
||||
},
|
||||
PatKind::Slice(front, wild_pat, back) => Self::Slice(
|
||||
arena.alloc_from_iter(front.iter().map(|pat| Self::from_pat(cx, arena, pat))),
|
||||
wild_pat.map(|_| &*arena.alloc_from_iter(back.iter().map(|pat| Self::from_pat(cx, arena, pat)))),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if two patterns overlap in the values they can match assuming they are for the same
|
||||
/// type.
|
||||
fn has_overlapping_values(&self, other: &Self) -> bool {
|
||||
match (*self, *other) {
|
||||
(Self::Wild, _) | (_, Self::Wild) => true,
|
||||
(Self::Or(pats), ref other) | (ref other, Self::Or(pats)) => {
|
||||
pats.iter().any(|pat| pat.has_overlapping_values(other))
|
||||
},
|
||||
(Self::Struct(lpath, lfields), Self::Struct(rpath, rfields)) => {
|
||||
if lpath != rpath {
|
||||
return false;
|
||||
}
|
||||
iter_matching_struct_fields(lfields, rfields).all(|(lpat, rpat)| lpat.has_overlapping_values(rpat))
|
||||
},
|
||||
(Self::Tuple(lpath, lpats), Self::Tuple(rpath, rpats)) => {
|
||||
if lpath != rpath {
|
||||
return false;
|
||||
}
|
||||
lpats
|
||||
.iter()
|
||||
.zip(rpats.iter())
|
||||
.all(|(lpat, rpat)| lpat.has_overlapping_values(rpat))
|
||||
},
|
||||
(Self::Path(x), Self::Path(y)) => x == y,
|
||||
(Self::LitStr(x), Self::LitStr(y)) => x == y,
|
||||
(Self::LitBytes(x), Self::LitBytes(y)) => x == y,
|
||||
(Self::LitInt(x), Self::LitInt(y)) => x == y,
|
||||
(Self::LitBool(x), Self::LitBool(y)) => x == y,
|
||||
(Self::Range(ref x), Self::Range(ref y)) => x.overlaps(y),
|
||||
(Self::Range(ref range), Self::LitInt(x)) | (Self::LitInt(x), Self::Range(ref range)) => range.contains(x),
|
||||
(Self::Slice(lpats, None), Self::Slice(rpats, None)) => {
|
||||
lpats.len() == rpats.len() && lpats.iter().zip(rpats.iter()).all(|(x, y)| x.has_overlapping_values(y))
|
||||
},
|
||||
(Self::Slice(pats, None), Self::Slice(front, Some(back)))
|
||||
| (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => {
|
||||
// Here `pats` is an exact size match. If the combined lengths of `front` and `back` are greater
|
||||
// then the minium length required will be greater than the length of `pats`.
|
||||
if pats.len() < front.len() + back.len() {
|
||||
return false;
|
||||
}
|
||||
pats[..front.len()]
|
||||
.iter()
|
||||
.zip(front.iter())
|
||||
.chain(pats[pats.len() - back.len()..].iter().zip(back.iter()))
|
||||
.all(|(x, y)| x.has_overlapping_values(y))
|
||||
},
|
||||
(Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => lfront
|
||||
.iter()
|
||||
.zip(rfront.iter())
|
||||
.chain(lback.iter().rev().zip(rback.iter().rev()))
|
||||
.all(|(x, y)| x.has_overlapping_values(y)),
|
||||
|
||||
// Enums can mix unit variants with tuple/struct variants. These can never overlap.
|
||||
(Self::Path(_), Self::Tuple(..) | Self::Struct(..))
|
||||
| (Self::Tuple(..) | Self::Struct(..), Self::Path(_)) => false,
|
||||
|
||||
// Tuples can be matched like a struct.
|
||||
(Self::Tuple(x, _), Self::Struct(y, _)) | (Self::Struct(x, _), Self::Tuple(y, _)) => {
|
||||
// TODO: check fields here.
|
||||
x == y
|
||||
},
|
||||
|
||||
// TODO: Lit* with Path, Range with Path, LitBytes with Slice
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,83 +1,73 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg_for_edges;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{symbol::sym, Span};
|
||||
|
||||
use super::MAP_FLATTEN;
|
||||
|
||||
/// lint use of `map().flatten()` for `Iterators` and 'Options'
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
map_arg: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
// lint if caller of `.map().flatten()` is an Iterator
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
let map_closure_ty = cx.typeck_results().expr_ty(map_arg);
|
||||
let is_map_to_option = match map_closure_ty.kind() {
|
||||
ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
|
||||
let map_closure_sig = match map_closure_ty.kind() {
|
||||
ty::Closure(_, substs) => substs.as_closure().sig(),
|
||||
_ => map_closure_ty.fn_sig(cx.tcx),
|
||||
};
|
||||
let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output());
|
||||
is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option)
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let method_to_use = if is_map_to_option {
|
||||
// `(...).map(...)` has type `impl Iterator<Item=Option<...>>
|
||||
"filter_map"
|
||||
} else {
|
||||
// `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
|
||||
"flat_map"
|
||||
};
|
||||
let func_snippet = snippet(cx, map_arg.span, "..");
|
||||
let hint = format!(".{0}({1})", method_to_use, func_snippet);
|
||||
span_lint_and_sugg(
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) {
|
||||
if let Some((caller_ty_name, method_to_use)) = try_get_caller_ty_name_and_method_name(cx, expr, recv, map_arg) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let help_msgs = [
|
||||
&format!("try replacing `map` with `{}`", method_to_use),
|
||||
"and remove the `.flatten()`",
|
||||
];
|
||||
let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability);
|
||||
span_lint_and_sugg_for_edges(
|
||||
cx,
|
||||
MAP_FLATTEN,
|
||||
expr.span.with_lo(recv.span.hi()),
|
||||
"called `map(..).flatten()` on an `Iterator`",
|
||||
&format!("try using `{}` instead", method_to_use),
|
||||
hint,
|
||||
Applicability::MachineApplicable,
|
||||
expr.span.with_lo(map_span.lo()),
|
||||
&format!("called `map(..).flatten()` on `{}`", caller_ty_name),
|
||||
&help_msgs,
|
||||
format!("{}({})", method_to_use, closure_snippet),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
// lint if caller of `.map().flatten()` is an Option or Result
|
||||
let caller_type = match cx.typeck_results().expr_ty(recv).kind() {
|
||||
ty::Adt(adt, _) => {
|
||||
if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) {
|
||||
"Option"
|
||||
} else if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) {
|
||||
"Result"
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
let func_snippet = snippet(cx, map_arg.span, "..");
|
||||
let hint = format!(".and_then({})", func_snippet);
|
||||
let lint_info = format!("called `map(..).flatten()` on an `{}`", caller_type);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_FLATTEN,
|
||||
expr.span.with_lo(recv.span.hi()),
|
||||
&lint_info,
|
||||
"try using `and_then` instead",
|
||||
hint,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
fn try_get_caller_ty_name_and_method_name(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
caller_expr: &Expr<'_>,
|
||||
map_arg: &Expr<'_>,
|
||||
) -> Option<(&'static str, &'static str)> {
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
if is_map_to_option(cx, map_arg) {
|
||||
// `(...).map(...)` has type `impl Iterator<Item=Option<...>>
|
||||
Some(("Iterator", "filter_map"))
|
||||
} else {
|
||||
// `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
|
||||
Some(("Iterator", "flat_map"))
|
||||
}
|
||||
} else {
|
||||
if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(caller_expr).kind() {
|
||||
if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) {
|
||||
return Some(("Option", "and_then"));
|
||||
} else if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) {
|
||||
return Some(("Result", "and_then"));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool {
|
||||
let map_closure_ty = cx.typeck_results().expr_ty(map_arg);
|
||||
match map_closure_ty.kind() {
|
||||
ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
|
||||
let map_closure_sig = match map_closure_ty.kind() {
|
||||
ty::Closure(_, substs) => substs.as_closure().sig(),
|
||||
_ => map_closure_ty.fn_sig(cx.tcx),
|
||||
};
|
||||
let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output());
|
||||
is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ mod option_as_ref_deref;
|
|||
mod option_map_or_none;
|
||||
mod option_map_unwrap_or;
|
||||
mod or_fun_call;
|
||||
mod or_then_unwrap;
|
||||
mod search_is_some;
|
||||
mod single_char_add_str;
|
||||
mod single_char_insert_string;
|
||||
|
@ -59,6 +60,7 @@ mod uninit_assumed_init;
|
|||
mod unnecessary_filter_map;
|
||||
mod unnecessary_fold;
|
||||
mod unnecessary_iter_cloned;
|
||||
mod unnecessary_join;
|
||||
mod unnecessary_lazy_eval;
|
||||
mod unnecessary_to_owned;
|
||||
mod unwrap_or_else_default;
|
||||
|
@ -778,6 +780,42 @@ declare_clippy_lint! {
|
|||
"using any `*or` method with a function call, which suggests `*or_else`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `.or(…).unwrap()` calls to Options and Results.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// You should use `.unwrap_or(…)` instead for clarity.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let fallback = "fallback";
|
||||
/// // Result
|
||||
/// # type Error = &'static str;
|
||||
/// # let result: Result<&str, Error> = Err("error");
|
||||
/// let value = result.or::<Error>(Ok(fallback)).unwrap();
|
||||
///
|
||||
/// // Option
|
||||
/// # let option: Option<&str> = None;
|
||||
/// let value = option.or(Some(fallback)).unwrap();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let fallback = "fallback";
|
||||
/// // Result
|
||||
/// # let result: Result<&str, &str> = Err("error");
|
||||
/// let value = result.unwrap_or(fallback);
|
||||
///
|
||||
/// // Option
|
||||
/// # let option: Option<&str> = None;
|
||||
/// let value = option.unwrap_or(fallback);
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub OR_THEN_UNWRAP,
|
||||
complexity,
|
||||
"checks for `.or(…).unwrap()` calls to Options and Results."
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
|
||||
|
@ -1140,7 +1178,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub ITER_WITH_DRAIN,
|
||||
perf,
|
||||
nursery,
|
||||
"replace `.drain(..)` with `.into_iter()`"
|
||||
}
|
||||
|
||||
|
@ -2012,6 +2050,35 @@ declare_clippy_lint! {
|
|||
"unnecessary calls to `to_owned`-like functions"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `.collect::<String>()` is more concise and usually more performant
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let vector = vec!["hello", "world"];
|
||||
/// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
|
||||
/// println!("{}", output);
|
||||
/// ```
|
||||
/// The correct use would be:
|
||||
/// ```rust
|
||||
/// let vector = vec!["hello", "world"];
|
||||
/// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
|
||||
/// println!("{}", output);
|
||||
/// ```
|
||||
/// ### Known problems
|
||||
/// While `.collect::<String>()` is more performant in most cases, there are cases where
|
||||
/// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
|
||||
/// will prevent loop unrolling and will result in a negative performance impact.
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub UNNECESSARY_JOIN,
|
||||
pedantic,
|
||||
"using `.collect::<Vec<String>>().join(\"\")` on an iterator"
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Option<RustcVersion>,
|
||||
|
@ -2039,6 +2106,7 @@ impl_lint_pass!(Methods => [
|
|||
OPTION_MAP_OR_NONE,
|
||||
BIND_INSTEAD_OF_MAP,
|
||||
OR_FUN_CALL,
|
||||
OR_THEN_UNWRAP,
|
||||
EXPECT_FUN_CALL,
|
||||
CHARS_NEXT_CMP,
|
||||
CHARS_LAST_CMP,
|
||||
|
@ -2096,6 +2164,7 @@ impl_lint_pass!(Methods => [
|
|||
MANUAL_SPLIT_ONCE,
|
||||
NEEDLESS_SPLITN,
|
||||
UNNECESSARY_TO_OWNED,
|
||||
UNNECESSARY_JOIN,
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
|
@ -2377,7 +2446,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
flat_map_option::check(cx, expr, arg, span);
|
||||
},
|
||||
(name @ "flatten", args @ []) => match method_call(recv) {
|
||||
Some(("map", [recv, map_arg], _)) => map_flatten::check(cx, expr, recv, map_arg),
|
||||
Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
|
||||
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
|
||||
_ => {},
|
||||
},
|
||||
|
@ -2391,6 +2460,11 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
("is_file", []) => filetype_is_file::check(cx, expr, recv),
|
||||
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
|
||||
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
|
||||
("join", [join_arg]) => {
|
||||
if let Some(("collect", _, span)) = method_call(recv) {
|
||||
unnecessary_join::check(cx, expr, recv, join_arg, span);
|
||||
}
|
||||
},
|
||||
("last", args @ []) | ("skip", args @ [_]) => {
|
||||
if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
|
||||
if let ("cloned", []) = (name2, args2) {
|
||||
|
@ -2474,6 +2548,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
Some(("get_mut", [recv, get_arg], _)) => {
|
||||
get_unwrap::check(cx, expr, recv, get_arg, true);
|
||||
},
|
||||
Some(("or", [recv, or_arg], or_span)) => {
|
||||
or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
unwrap_used::check(cx, expr, recv);
|
||||
|
|
68
clippy_lints/src/methods/or_then_unwrap.rs
Normal file
68
clippy_lints/src/methods/or_then_unwrap.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{diagnostics::span_lint_and_sugg, is_lang_ctor};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{lang_items::LangItem, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::OR_THEN_UNWRAP;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
unwrap_expr: &Expr<'_>,
|
||||
recv: &'tcx Expr<'tcx>,
|
||||
or_arg: &'tcx Expr<'_>,
|
||||
or_span: Span,
|
||||
) {
|
||||
let ty = cx.typeck_results().expr_ty(recv); // get type of x (we later check if it's Option or Result)
|
||||
let title;
|
||||
let or_arg_content: Span;
|
||||
|
||||
if is_type_diagnostic_item(cx, ty, sym::Option) {
|
||||
title = "found `.or(Some(…)).unwrap()`";
|
||||
if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::OptionSome) {
|
||||
or_arg_content = content;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::Result) {
|
||||
title = "found `.or(Ok(…)).unwrap()`";
|
||||
if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::ResultOk) {
|
||||
or_arg_content = content;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Someone has implemented a struct with .or(...).unwrap() chaining,
|
||||
// but it's not an Option or a Result, so bail
|
||||
return;
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let suggestion = format!(
|
||||
"unwrap_or({})",
|
||||
snippet_with_applicability(cx, or_arg_content, "..", &mut applicability)
|
||||
);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
OR_THEN_UNWRAP,
|
||||
unwrap_expr.span.with_lo(or_span.lo()),
|
||||
title,
|
||||
"try this",
|
||||
suggestion,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: LangItem) -> Option<Span> {
|
||||
if let ExprKind::Call(some_expr, [arg]) = expr.kind
|
||||
&& let ExprKind::Path(qpath) = &some_expr.kind
|
||||
&& is_lang_ctor(cx, qpath, item)
|
||||
{
|
||||
Some(arg.span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
41
clippy_lints/src/methods/unnecessary_join.rs
Normal file
41
clippy_lints/src/methods/unnecessary_join.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{Ref, Slice};
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::UNNECESSARY_JOIN;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
join_self_arg: &'tcx Expr<'tcx>,
|
||||
join_arg: &'tcx Expr<'tcx>,
|
||||
span: Span,
|
||||
) {
|
||||
let applicability = Applicability::MachineApplicable;
|
||||
let collect_output_adjusted_type = cx.typeck_results().expr_ty_adjusted(join_self_arg);
|
||||
if_chain! {
|
||||
// the turbofish for collect is ::<Vec<String>>
|
||||
if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind();
|
||||
if let Slice(slice) = ref_type.kind();
|
||||
if is_type_diagnostic_item(cx, *slice, sym::String);
|
||||
// the argument for join is ""
|
||||
if let ExprKind::Lit(spanned) = &join_arg.kind;
|
||||
if let LitKind::Str(symbol, _) = spanned.node;
|
||||
if symbol.is_empty();
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_JOIN,
|
||||
span.with_hi(expr.span.hi()),
|
||||
r#"called `.collect<Vec<String>>().join("")` on an iterator"#,
|
||||
"try using",
|
||||
"collect::<String>()".to_owned(),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{eager_or_lazy, usage};
|
||||
|
@ -48,20 +48,19 @@ pub(super) fn check<'tcx>(
|
|||
Applicability::MaybeIncorrect
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_LAZY_EVALUATIONS,
|
||||
expr.span,
|
||||
msg,
|
||||
&format!("use `{}` instead", simplify_using),
|
||||
format!(
|
||||
"{0}.{1}({2})",
|
||||
snippet(cx, recv.span, ".."),
|
||||
simplify_using,
|
||||
snippet(cx, body_expr.span, ".."),
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
// This is a duplicate of what's happening in clippy_lints::methods::method_call,
|
||||
// which isn't ideal, We want to get the method call span,
|
||||
// but prefer to avoid changing the signature of the function itself.
|
||||
if let hir::ExprKind::MethodCall(_, _, span) = expr.kind {
|
||||
span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
&format!("use `{}(..)` instead", simplify_using),
|
||||
format!("{}({})", simplify_using, snippet(cx, body_expr.span, "..")),
|
||||
applicability,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ use super::implicit_clone::is_clone_like;
|
|||
use super::unnecessary_iter_cloned::{self, is_into_iter};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
|
||||
use clippy_utils::ty::{
|
||||
contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
|
||||
};
|
||||
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
|
||||
|
@ -114,7 +116,12 @@ fn check_addr_of_expr(
|
|||
parent.span,
|
||||
&format!("unnecessary use of `{}`", method_name),
|
||||
"use",
|
||||
format!("{:&>width$}{}", "", receiver_snippet, width = n_target_refs - n_receiver_refs),
|
||||
format!(
|
||||
"{:&>width$}{}",
|
||||
"",
|
||||
receiver_snippet,
|
||||
width = n_target_refs - n_receiver_refs
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return true;
|
||||
|
@ -182,20 +189,10 @@ fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name:
|
|||
if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
|
||||
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
then {
|
||||
if unnecessary_iter_cloned::check_for_loop_iter(
|
||||
cx,
|
||||
parent,
|
||||
method_name,
|
||||
receiver,
|
||||
true,
|
||||
) {
|
||||
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
|
||||
return true;
|
||||
}
|
||||
let cloned_or_copied = if is_copy(cx, item_ty) {
|
||||
"copied"
|
||||
} else {
|
||||
"cloned"
|
||||
};
|
||||
let cloned_or_copied = if is_copy(cx, item_ty) { "copied" } else { "cloned" };
|
||||
// The next suggestion may be incorrect because the removal of the `to_owned`-like
|
||||
// function could cause the iterator to hold a reference to a resource that is used
|
||||
// mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
|
||||
|
@ -243,10 +240,11 @@ fn check_other_call_arg<'tcx>(
|
|||
if if trait_predicate.def_id() == deref_trait_id {
|
||||
if let [projection_predicate] = projection_predicates[..] {
|
||||
let normalized_ty =
|
||||
cx.tcx.subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term);
|
||||
cx.tcx
|
||||
.subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term);
|
||||
implements_trait(cx, receiver_ty, deref_trait_id, &[])
|
||||
&& get_associated_type(cx, receiver_ty, deref_trait_id,
|
||||
"Target").map_or(false, |ty| ty::Term::Ty(ty) == normalized_ty)
|
||||
&& get_associated_type(cx, receiver_ty, deref_trait_id, "Target")
|
||||
.map_or(false, |ty| ty::Term::Ty(ty) == normalized_ty)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -254,7 +252,7 @@ fn check_other_call_arg<'tcx>(
|
|||
let composed_substs = compose_substs(
|
||||
cx,
|
||||
&trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
|
||||
call_substs
|
||||
call_substs,
|
||||
);
|
||||
implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
|
||||
} else {
|
||||
|
@ -264,6 +262,12 @@ fn check_other_call_arg<'tcx>(
|
|||
// `Target = T`.
|
||||
if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
|
||||
let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 });
|
||||
// If the trait is `AsRef` and the input type variable `T` occurs in the output type, then
|
||||
// `T` must not be instantiated with a reference
|
||||
// (https://github.com/rust-lang/rust-clippy/issues/8507).
|
||||
if (n_refs == 0 && !receiver_ty.is_ref())
|
||||
|| trait_predicate.def_id() != as_ref_trait_id
|
||||
|| !contains_ty(fn_sig.output(), input);
|
||||
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
|
@ -339,11 +343,7 @@ fn get_input_traits_and_projections<'tcx>(
|
|||
if let Some(arg) = substs.iter().next();
|
||||
if let GenericArgKind::Type(arg_ty) = arg.unpack();
|
||||
if arg_ty == input;
|
||||
then {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
then { true } else { false }
|
||||
}
|
||||
};
|
||||
match predicate.kind().skip_binder() {
|
||||
|
|
|
@ -436,7 +436,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
|
|||
DerefTy::Path,
|
||||
None,
|
||||
),
|
||||
Some(sym::Cow) => {
|
||||
Some(sym::Cow) if mutability == Mutability::Not => {
|
||||
let ty_name = name.args
|
||||
.and_then(|args| {
|
||||
args.args.iter().find_map(|a| match a {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
|
||||
use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind};
|
||||
use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -76,14 +76,13 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
|
|||
);
|
||||
}
|
||||
|
||||
for single_use in &single_use_usages {
|
||||
if !imports_reused_with_self.contains(&single_use.0) {
|
||||
let can_suggest = single_use.2;
|
||||
for (name, span, can_suggest) in single_use_usages {
|
||||
if !imports_reused_with_self.contains(&name) {
|
||||
if can_suggest {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SINGLE_COMPONENT_PATH_IMPORTS,
|
||||
single_use.1,
|
||||
span,
|
||||
"this import is redundant",
|
||||
"remove it entirely",
|
||||
String::new(),
|
||||
|
@ -93,7 +92,7 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
|
|||
span_lint_and_help(
|
||||
cx,
|
||||
SINGLE_COMPONENT_PATH_IMPORTS,
|
||||
single_use.1,
|
||||
span,
|
||||
"this import is redundant",
|
||||
None,
|
||||
"remove this import",
|
||||
|
@ -124,14 +123,11 @@ fn track_uses(
|
|||
ItemKind::Use(use_tree) => {
|
||||
let segments = &use_tree.prefix.segments;
|
||||
|
||||
let should_report =
|
||||
|name: &Symbol| !macros.contains(name) || matches!(item.vis.kind, VisibilityKind::Inherited);
|
||||
|
||||
// keep track of `use some_module;` usages
|
||||
if segments.len() == 1 {
|
||||
if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
|
||||
let name = segments[0].ident.name;
|
||||
if should_report(&name) {
|
||||
if !macros.contains(&name) {
|
||||
single_use_usages.push((name, item.span, true));
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +142,7 @@ fn track_uses(
|
|||
if segments.len() == 1 {
|
||||
if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
|
||||
let name = segments[0].ident.name;
|
||||
if should_report(&name) {
|
||||
if !macros.contains(&name) {
|
||||
single_use_usages.push((name, tree.0.span, false));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -415,7 +415,8 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
|
|||
// And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
|
||||
let const_context = in_constant(cx, e.hir_id);
|
||||
|
||||
let from_ty = cx.typeck_results().expr_ty(arg);
|
||||
let from_ty = cx.typeck_results().expr_ty_adjusted(arg);
|
||||
// Adjustments for `to_ty` happen after the call to `transmute`, so don't use them.
|
||||
let to_ty = cx.typeck_results().expr_ty(e);
|
||||
|
||||
// If useless_transmute is triggered, the other lints can be skipped.
|
||||
|
|
|
@ -3,8 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::ty::is_c_void;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::subst::Subst;
|
||||
use rustc_middle::ty::{self, Ty, TypeAndMut};
|
||||
use rustc_middle::ty::subst::{Subst, SubstsRef};
|
||||
use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy};
|
||||
use rustc_span::Span;
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
|
@ -23,7 +23,8 @@ pub(super) fn check<'tcx>(
|
|||
unsized_ty,
|
||||
to_ty: to_sub_ty,
|
||||
} => match reduce_ty(cx, to_sub_ty) {
|
||||
ReducedTy::IntArray | ReducedTy::TypeErasure => break,
|
||||
ReducedTy::TypeErasure => break,
|
||||
ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
|
||||
ReducedTy::Ref(to_sub_ty) => {
|
||||
from_ty = unsized_ty;
|
||||
to_ty = to_sub_ty;
|
||||
|
@ -48,7 +49,8 @@ pub(super) fn check<'tcx>(
|
|||
unsized_ty,
|
||||
from_ty: from_sub_ty,
|
||||
} => match reduce_ty(cx, from_sub_ty) {
|
||||
ReducedTy::IntArray | ReducedTy::TypeErasure => break,
|
||||
ReducedTy::TypeErasure => break,
|
||||
ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
|
||||
ReducedTy::Ref(from_sub_ty) => {
|
||||
from_ty = from_sub_ty;
|
||||
to_ty = unsized_ty;
|
||||
|
@ -123,9 +125,19 @@ pub(super) fn check<'tcx>(
|
|||
from_ty: from_sub_ty,
|
||||
to_ty: to_sub_ty,
|
||||
} => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
|
||||
(ReducedTy::IntArray | ReducedTy::TypeErasure, _)
|
||||
| (_, ReducedTy::IntArray | ReducedTy::TypeErasure) => return false,
|
||||
(ReducedTy::TypeErasure, _) | (_, ReducedTy::TypeErasure) => return false,
|
||||
(ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
|
||||
let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
|
||||
= (from_ty.kind(), to_ty.kind())
|
||||
&& from_def == to_def
|
||||
{
|
||||
if same_except_params(from_subs, to_subs) {
|
||||
return false;
|
||||
}
|
||||
Some(from_def.did())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
|
@ -135,21 +147,17 @@ pub(super) fn check<'tcx>(
|
|||
from_ty_orig, to_ty_orig
|
||||
),
|
||||
|diag| {
|
||||
if_chain! {
|
||||
if let (Some(from_def), Some(to_def)) = (from_ty.ty_adt_def(), to_ty.ty_adt_def());
|
||||
if from_def == to_def;
|
||||
then {
|
||||
diag.note(&format!(
|
||||
"two instances of the same generic type (`{}`) may have different layouts",
|
||||
cx.tcx.item_name(from_def.did())
|
||||
));
|
||||
} else {
|
||||
if from_ty_orig.peel_refs() != from_ty {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
|
||||
}
|
||||
if to_ty_orig.peel_refs() != to_ty {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
|
||||
}
|
||||
if let Some(same_adt_did) = same_adt_did {
|
||||
diag.note(&format!(
|
||||
"two instances of the same generic type (`{}`) may have different layouts",
|
||||
cx.tcx.item_name(same_adt_did)
|
||||
));
|
||||
} else {
|
||||
if from_ty_orig.peel_refs() != from_ty {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
|
||||
}
|
||||
if to_ty_orig.peel_refs() != to_ty {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -196,10 +204,13 @@ pub(super) fn check<'tcx>(
|
|||
continue;
|
||||
},
|
||||
(
|
||||
ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
|
||||
ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
|
||||
ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
|
||||
ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
|
||||
)
|
||||
| (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => break,
|
||||
| (
|
||||
ReducedTy::UnorderedFields(_) | ReducedTy::Param,
|
||||
ReducedTy::UnorderedFields(_) | ReducedTy::Param,
|
||||
) => break,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -263,9 +274,8 @@ enum ReducedTy<'tcx> {
|
|||
UnorderedFields(Ty<'tcx>),
|
||||
/// The type is a reference to the contained type.
|
||||
Ref(Ty<'tcx>),
|
||||
/// The type is an array of a primitive integer type. These can be used as storage for a value
|
||||
/// of another type.
|
||||
IntArray,
|
||||
/// The type is a generic parameter.
|
||||
Param,
|
||||
/// Any other type.
|
||||
Other(Ty<'tcx>),
|
||||
}
|
||||
|
@ -275,17 +285,18 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
|
|||
loop {
|
||||
ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
|
||||
return match *ty.kind() {
|
||||
ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::IntArray,
|
||||
ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::TypeErasure,
|
||||
ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
|
||||
ty = sub_ty;
|
||||
continue;
|
||||
},
|
||||
ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure,
|
||||
ty::Tuple(args) => {
|
||||
let Some(sized_ty) = args.iter().find(|&ty| !is_zero_sized_ty(cx, ty)) else {
|
||||
let mut iter = args.iter();
|
||||
let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
|
||||
return ReducedTy::OrderedFields(ty);
|
||||
};
|
||||
if args.iter().all(|ty| is_zero_sized_ty(cx, ty)) {
|
||||
if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
|
||||
ty = sized_ty;
|
||||
continue;
|
||||
}
|
||||
|
@ -313,9 +324,12 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
|
|||
ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => {
|
||||
ReducedTy::TypeErasure
|
||||
},
|
||||
// TODO: Check if the conversion to or from at least one of a union's fields is valid.
|
||||
ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure,
|
||||
ty::Foreign(_) => ReducedTy::TypeErasure,
|
||||
ty::Ref(_, ty, _) => ReducedTy::Ref(ty),
|
||||
ty::RawPtr(ty) => ReducedTy::Ref(ty.ty),
|
||||
ty::Param(_) => ReducedTy::Param,
|
||||
_ => ReducedTy::Other(ty),
|
||||
};
|
||||
}
|
||||
|
@ -332,3 +346,27 @@ fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_size_pair(ty: Ty<'_>) -> bool {
|
||||
if let ty::Tuple(tys) = *ty.kind()
|
||||
&& let [ty1, ty2] = &**tys
|
||||
{
|
||||
matches!(ty1.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
|
||||
&& matches!(ty2.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn same_except_params(subs1: SubstsRef<'_>, subs2: SubstsRef<'_>) -> bool {
|
||||
// TODO: check const parameters as well. Currently this will consider `Array<5>` the same as
|
||||
// `Array<6>`
|
||||
for (ty1, ty2) in subs1.types().zip(subs2.types()).filter(|(ty1, ty2)| ty1 != ty2) {
|
||||
match (ty1.kind(), ty2.kind()) {
|
||||
(ty::Param(_), _) | (_, ty::Param(_)) => (),
|
||||
(ty::Adt(adt1, subs1), ty::Adt(adt2, subs2)) if adt1 == adt2 && same_except_params(subs1, subs2) => (),
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.38.0"]
|
||||
pub TRY_ERR,
|
||||
style,
|
||||
restriction,
|
||||
"return errors explicitly rather than hiding them behind a `?`"
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ macro_rules! CONFIGURATION_VALUE_TEMPLATE {
|
|||
};
|
||||
}
|
||||
|
||||
const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
|
||||
const LINT_EMISSION_FUNCTIONS: [&[&str]; 8] = [
|
||||
&["clippy_utils", "diagnostics", "span_lint"],
|
||||
&["clippy_utils", "diagnostics", "span_lint_and_help"],
|
||||
&["clippy_utils", "diagnostics", "span_lint_and_note"],
|
||||
|
@ -93,6 +93,7 @@ const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
|
|||
&["clippy_utils", "diagnostics", "span_lint_and_sugg"],
|
||||
&["clippy_utils", "diagnostics", "span_lint_and_then"],
|
||||
&["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
|
||||
&["clippy_utils", "diagnostics", "span_lint_and_sugg_for_edges"],
|
||||
];
|
||||
const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [
|
||||
("span_suggestion", false),
|
||||
|
|
|
@ -581,14 +581,19 @@ impl Write {
|
|||
};
|
||||
|
||||
let replacement: String = match lit.token.kind {
|
||||
LitKind::Integer | LitKind::Float | LitKind::Err => continue,
|
||||
LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
|
||||
lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
|
||||
},
|
||||
LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
|
||||
lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
|
||||
},
|
||||
LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue,
|
||||
LitKind::StrRaw(_)
|
||||
| LitKind::Str
|
||||
| LitKind::ByteStrRaw(_)
|
||||
| LitKind::ByteStr
|
||||
| LitKind::Integer
|
||||
| LitKind::Float
|
||||
| LitKind::Err => continue,
|
||||
LitKind::Byte | LitKind::Char => match lit.token.symbol.as_str() {
|
||||
"\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"",
|
||||
"\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//! Thank you!
|
||||
//! ~The `INTERNAL_METADATA_COLLECTOR` lint
|
||||
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_errors::{emitter::MAX_SUGGESTION_HIGHLIGHT_LINES, Applicability, Diagnostic};
|
||||
use rustc_hir::HirId;
|
||||
use rustc_lint::{LateContext, Lint, LintContext};
|
||||
use rustc_span::source_map::{MultiSpan, Span};
|
||||
|
@ -213,6 +213,90 @@ pub fn span_lint_and_sugg<'a, T: LintContext>(
|
|||
});
|
||||
}
|
||||
|
||||
/// Like [`span_lint_and_sugg`] with a focus on the edges. The output will either
|
||||
/// emit single span or multispan suggestion depending on the number of its lines.
|
||||
///
|
||||
/// If the given suggestion string has more lines than the maximum display length defined by
|
||||
/// [`MAX_SUGGESTION_HIGHLIGHT_LINES`][`rustc_errors::emitter::MAX_SUGGESTION_HIGHLIGHT_LINES`],
|
||||
/// this function will split the suggestion and span to showcase the change for the top and
|
||||
/// bottom edge of the code. For normal suggestions, in one display window, the help message
|
||||
/// will be combined with a colon.
|
||||
///
|
||||
/// Multipart suggestions like the one being created here currently cannot be
|
||||
/// applied by rustfix (See [rustfix#141](https://github.com/rust-lang/rustfix/issues/141)).
|
||||
/// Testing rustfix with this lint emission function might require a file with
|
||||
/// suggestions that can be fixed and those that can't. See
|
||||
/// [clippy#8520](https://github.com/rust-lang/rust-clippy/pull/8520/files) for
|
||||
/// an example and of this.
|
||||
///
|
||||
/// # Example for a long suggestion
|
||||
///
|
||||
/// ```text
|
||||
/// error: called `map(..).flatten()` on `Option`
|
||||
/// --> $DIR/map_flatten.rs:8:10
|
||||
/// |
|
||||
/// LL | .map(|x| {
|
||||
/// | __________^
|
||||
/// LL | | if x <= 5 {
|
||||
/// LL | | Some(x)
|
||||
/// LL | | } else {
|
||||
/// ... |
|
||||
/// LL | | })
|
||||
/// LL | | .flatten();
|
||||
/// | |__________________^
|
||||
/// |
|
||||
/// = note: `-D clippy::map-flatten` implied by `-D warnings`
|
||||
/// help: try replacing `map` with `and_then`
|
||||
/// |
|
||||
/// LL ~ .and_then(|x| {
|
||||
/// LL + if x <= 5 {
|
||||
/// LL + Some(x)
|
||||
/// |
|
||||
/// help: and remove the `.flatten()`
|
||||
/// |
|
||||
/// LL + None
|
||||
/// LL + }
|
||||
/// LL ~ });
|
||||
/// |
|
||||
/// ```
|
||||
pub fn span_lint_and_sugg_for_edges(
|
||||
cx: &LateContext<'_>,
|
||||
lint: &'static Lint,
|
||||
sp: Span,
|
||||
msg: &str,
|
||||
helps: &[&str; 2],
|
||||
sugg: String,
|
||||
applicability: Applicability,
|
||||
) {
|
||||
span_lint_and_then(cx, lint, sp, msg, |diag| {
|
||||
let sugg_lines_count = sugg.lines().count();
|
||||
if sugg_lines_count > MAX_SUGGESTION_HIGHLIGHT_LINES {
|
||||
let sm = cx.sess().source_map();
|
||||
if let (Ok(line_upper), Ok(line_bottom)) = (sm.lookup_line(sp.lo()), sm.lookup_line(sp.hi())) {
|
||||
let split_idx = MAX_SUGGESTION_HIGHLIGHT_LINES / 2;
|
||||
let span_upper = sm.span_until_char(sp.with_hi(line_upper.sf.lines[line_upper.line + split_idx]), '\n');
|
||||
let span_bottom = sp.with_lo(line_bottom.sf.lines[line_bottom.line - split_idx]);
|
||||
|
||||
let sugg_lines_vec = sugg.lines().collect::<Vec<&str>>();
|
||||
let sugg_upper = sugg_lines_vec[..split_idx].join("\n");
|
||||
let sugg_bottom = sugg_lines_vec[sugg_lines_count - split_idx..].join("\n");
|
||||
|
||||
diag.span_suggestion(span_upper, helps[0], sugg_upper, applicability);
|
||||
diag.span_suggestion(span_bottom, helps[1], sugg_bottom, applicability);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
diag.span_suggestion_with_style(
|
||||
sp,
|
||||
&helps.join(", "),
|
||||
sugg,
|
||||
applicability,
|
||||
rustc_errors::SuggestionStyle::ShowAlways,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a suggestion made from several `span → replacement`.
|
||||
///
|
||||
/// Note: in the JSON format (used by `compiletest_rs`), the help message will
|
||||
|
|
|
@ -808,7 +808,7 @@ pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'
|
|||
closure_arg_is_type_annotated_double_ref,
|
||||
next_pos: closure.span.lo(),
|
||||
suggestion_start: String::new(),
|
||||
applicability: Applicability::MaybeIncorrect,
|
||||
applicability: Applicability::MachineApplicable,
|
||||
};
|
||||
|
||||
let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id);
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "nightly-2022-03-14"
|
||||
channel = "nightly-2022-03-24"
|
||||
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||
|
|
17
tests/ui/cast_enum_constructor.rs
Normal file
17
tests/ui/cast_enum_constructor.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
#![warn(clippy::cast_enum_constructor)]
|
||||
#![allow(clippy::fn_to_numeric_cast)]
|
||||
|
||||
fn main() {
|
||||
enum Foo {
|
||||
Y(u32),
|
||||
}
|
||||
|
||||
enum Bar {
|
||||
X,
|
||||
}
|
||||
|
||||
let _ = Foo::Y as usize;
|
||||
let _ = Foo::Y as isize;
|
||||
let _ = Foo::Y as fn(u32) -> Foo;
|
||||
let _ = Bar::X as usize;
|
||||
}
|
16
tests/ui/cast_enum_constructor.stderr
Normal file
16
tests/ui/cast_enum_constructor.stderr
Normal file
|
@ -0,0 +1,16 @@
|
|||
error: cast of an enum tuple constructor to an integer
|
||||
--> $DIR/cast_enum_constructor.rs:13:13
|
||||
|
|
||||
LL | let _ = Foo::Y as usize;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::cast-enum-constructor` implied by `-D warnings`
|
||||
|
||||
error: cast of an enum tuple constructor to an integer
|
||||
--> $DIR/cast_enum_constructor.rs:14:13
|
||||
|
|
||||
LL | let _ = Foo::Y as isize;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -1,31 +1,55 @@
|
|||
// run-rustfix
|
||||
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
#![allow(clippy::let_underscore_drop)]
|
||||
#![allow(clippy::missing_docs_in_private_items)]
|
||||
#![allow(clippy::map_identity)]
|
||||
#![allow(clippy::redundant_closure)]
|
||||
#![allow(clippy::unnecessary_wraps)]
|
||||
#![warn(clippy::map_flatten)]
|
||||
#![feature(result_flattening)]
|
||||
|
||||
fn main() {
|
||||
// mapping to Option on Iterator
|
||||
fn option_id(x: i8) -> Option<i8> {
|
||||
Some(x)
|
||||
}
|
||||
let option_id_ref: fn(i8) -> Option<i8> = option_id;
|
||||
let option_id_closure = |x| Some(x);
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
|
||||
// issue #8506, multi-line
|
||||
#[rustfmt::skip]
|
||||
fn long_span() {
|
||||
let _: Option<i32> = Some(1)
|
||||
.map(|x| {
|
||||
if x <= 5 {
|
||||
Some(x)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.flatten();
|
||||
|
||||
// mapping to Iterator on Iterator
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
|
||||
let _: Result<i32, i32> = Ok(1)
|
||||
.map(|x| {
|
||||
if x == 1 {
|
||||
Ok(x)
|
||||
} else {
|
||||
Err(0)
|
||||
}
|
||||
})
|
||||
.flatten();
|
||||
|
||||
// mapping to Option on Option
|
||||
let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
|
||||
|
||||
// mapping to Result on Result
|
||||
let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
|
||||
let result: Result<i32, i32> = Ok(2);
|
||||
fn do_something() { }
|
||||
let _: Result<i32, i32> = result
|
||||
.map(|res| {
|
||||
if res > 0 {
|
||||
do_something();
|
||||
Ok(res)
|
||||
} else {
|
||||
Err(0)
|
||||
}
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let _: Vec<_> = vec![5_i8; 6]
|
||||
.into_iter()
|
||||
.map(|some_value| {
|
||||
if some_value > 3 {
|
||||
Some(some_value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.collect();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
long_span();
|
||||
}
|
||||
|
|
|
@ -1,46 +1,107 @@
|
|||
error: called `map(..).flatten()` on an `Iterator`
|
||||
--> $DIR/map_flatten.rs:18:46
|
||||
error: called `map(..).flatten()` on `Option`
|
||||
--> $DIR/map_flatten.rs:8:10
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)`
|
||||
LL | .map(|x| {
|
||||
| __________^
|
||||
LL | | if x <= 5 {
|
||||
LL | | Some(x)
|
||||
LL | | } else {
|
||||
... |
|
||||
LL | | })
|
||||
LL | | .flatten();
|
||||
| |__________________^
|
||||
|
|
||||
= note: `-D clippy::map-flatten` implied by `-D warnings`
|
||||
|
||||
error: called `map(..).flatten()` on an `Iterator`
|
||||
--> $DIR/map_flatten.rs:19:46
|
||||
help: try replacing `map` with `and_then`
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)`
|
||||
|
||||
error: called `map(..).flatten()` on an `Iterator`
|
||||
--> $DIR/map_flatten.rs:20:46
|
||||
LL ~ .and_then(|x| {
|
||||
LL + if x <= 5 {
|
||||
LL + Some(x)
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)`
|
||||
|
||||
error: called `map(..).flatten()` on an `Iterator`
|
||||
--> $DIR/map_flatten.rs:21:46
|
||||
help: and remove the `.flatten()`
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))`
|
||||
|
||||
error: called `map(..).flatten()` on an `Iterator`
|
||||
--> $DIR/map_flatten.rs:24:46
|
||||
LL + None
|
||||
LL + }
|
||||
LL ~ });
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)`
|
||||
|
||||
error: called `map(..).flatten()` on an `Option`
|
||||
--> $DIR/map_flatten.rs:27:39
|
||||
error: called `map(..).flatten()` on `Result`
|
||||
--> $DIR/map_flatten.rs:18:10
|
||||
|
|
||||
LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
|
||||
|
||||
error: called `map(..).flatten()` on an `Result`
|
||||
--> $DIR/map_flatten.rs:30:41
|
||||
LL | .map(|x| {
|
||||
| __________^
|
||||
LL | | if x == 1 {
|
||||
LL | | Ok(x)
|
||||
LL | | } else {
|
||||
... |
|
||||
LL | | })
|
||||
LL | | .flatten();
|
||||
| |__________________^
|
||||
|
|
||||
help: try replacing `map` with `and_then`
|
||||
|
|
||||
LL ~ .and_then(|x| {
|
||||
LL + if x == 1 {
|
||||
LL + Ok(x)
|
||||
|
|
||||
help: and remove the `.flatten()`
|
||||
|
|
||||
LL + Err(0)
|
||||
LL + }
|
||||
LL ~ });
|
||||
|
|
||||
LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
error: called `map(..).flatten()` on `Result`
|
||||
--> $DIR/map_flatten.rs:30:10
|
||||
|
|
||||
LL | .map(|res| {
|
||||
| __________^
|
||||
LL | | if res > 0 {
|
||||
LL | | do_something();
|
||||
LL | | Ok(res)
|
||||
... |
|
||||
LL | | })
|
||||
LL | | .flatten();
|
||||
| |__________________^
|
||||
|
|
||||
help: try replacing `map` with `and_then`
|
||||
|
|
||||
LL ~ .and_then(|res| {
|
||||
LL + if res > 0 {
|
||||
LL + do_something();
|
||||
|
|
||||
help: and remove the `.flatten()`
|
||||
|
|
||||
LL + Err(0)
|
||||
LL + }
|
||||
LL ~ });
|
||||
|
|
||||
|
||||
error: called `map(..).flatten()` on `Iterator`
|
||||
--> $DIR/map_flatten.rs:42:10
|
||||
|
|
||||
LL | .map(|some_value| {
|
||||
| __________^
|
||||
LL | | if some_value > 3 {
|
||||
LL | | Some(some_value)
|
||||
LL | | } else {
|
||||
... |
|
||||
LL | | })
|
||||
LL | | .flatten()
|
||||
| |__________________^
|
||||
|
|
||||
help: try replacing `map` with `filter_map`
|
||||
|
|
||||
LL ~ .filter_map(|some_value| {
|
||||
LL + if some_value > 3 {
|
||||
LL + Some(some_value)
|
||||
|
|
||||
help: and remove the `.flatten()`
|
||||
|
|
||||
LL + None
|
||||
LL + }
|
||||
LL + })
|
||||
|
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
31
tests/ui/map_flatten_fixable.rs
Normal file
31
tests/ui/map_flatten_fixable.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
// run-rustfix
|
||||
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
#![allow(clippy::let_underscore_drop)]
|
||||
#![allow(clippy::missing_docs_in_private_items)]
|
||||
#![allow(clippy::map_identity)]
|
||||
#![allow(clippy::redundant_closure)]
|
||||
#![allow(clippy::unnecessary_wraps)]
|
||||
#![feature(result_flattening)]
|
||||
|
||||
fn main() {
|
||||
// mapping to Option on Iterator
|
||||
fn option_id(x: i8) -> Option<i8> {
|
||||
Some(x)
|
||||
}
|
||||
let option_id_ref: fn(i8) -> Option<i8> = option_id;
|
||||
let option_id_closure = |x| Some(x);
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
|
||||
|
||||
// mapping to Iterator on Iterator
|
||||
let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
|
||||
|
||||
// mapping to Option on Option
|
||||
let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
|
||||
|
||||
// mapping to Result on Result
|
||||
let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
|
||||
}
|
80
tests/ui/map_flatten_fixable.stderr
Normal file
80
tests/ui/map_flatten_fixable.stderr
Normal file
|
@ -0,0 +1,80 @@
|
|||
error: called `map(..).flatten()` on `Iterator`
|
||||
--> $DIR/map_flatten_fixable.rs:18:47
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::map-flatten` implied by `-D warnings`
|
||||
help: try replacing `map` with `filter_map`, and remove the `.flatten()`
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: called `map(..).flatten()` on `Iterator`
|
||||
--> $DIR/map_flatten_fixable.rs:19:47
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try replacing `map` with `filter_map`, and remove the `.flatten()`
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: called `map(..).flatten()` on `Iterator`
|
||||
--> $DIR/map_flatten_fixable.rs:20:47
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try replacing `map` with `filter_map`, and remove the `.flatten()`
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: called `map(..).flatten()` on `Iterator`
|
||||
--> $DIR/map_flatten_fixable.rs:21:47
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try replacing `map` with `filter_map`, and remove the `.flatten()`
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: called `map(..).flatten()` on `Iterator`
|
||||
--> $DIR/map_flatten_fixable.rs:24:47
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try replacing `map` with `flat_map`, and remove the `.flatten()`
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: called `map(..).flatten()` on `Option`
|
||||
--> $DIR/map_flatten_fixable.rs:27:40
|
||||
|
|
||||
LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try replacing `map` with `and_then`, and remove the `.flatten()`
|
||||
|
|
||||
LL | let _: Option<_> = (Some(Some(1))).and_then(|x| x);
|
||||
| ~~~~~~~~~~~~~~~
|
||||
|
||||
error: called `map(..).flatten()` on `Result`
|
||||
--> $DIR/map_flatten_fixable.rs:30:42
|
||||
|
|
||||
LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try replacing `map` with `and_then`, and remove the `.flatten()`
|
||||
|
|
||||
LL | let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x);
|
||||
| ~~~~~~~~~~~~~~~
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
|
@ -1,128 +1,121 @@
|
|||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms.rs:13:14
|
||||
error: this match arm has an identical body to the `_` wildcard arm
|
||||
--> $DIR/match_same_arms.rs:11:9
|
||||
|
|
||||
LL | _ => 0, //~ ERROR match arms have same body
|
||||
| ^
|
||||
LL | Abc::A => 0,
|
||||
| ^^^^^^^^^^^ help: try removing the arm
|
||||
|
|
||||
= note: `-D clippy::match-same-arms` implied by `-D warnings`
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms.rs:11:19
|
||||
= help: or try changing either arm body
|
||||
note: `_` wildcard arm here
|
||||
--> $DIR/match_same_arms.rs:13:9
|
||||
|
|
||||
LL | Abc::A => 0,
|
||||
| ^
|
||||
note: `Abc::A` has the same arm body as the `_` wildcard, consider removing it
|
||||
--> $DIR/match_same_arms.rs:11:19
|
||||
|
|
||||
LL | Abc::A => 0,
|
||||
| ^
|
||||
LL | _ => 0, //~ ERROR match arms have same body
|
||||
| ^^^^^^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms.rs:18:20
|
||||
|
|
||||
LL | (.., 3) => 42, //~ ERROR match arms have same body
|
||||
| ^^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms.rs:17:23
|
||||
|
|
||||
LL | (1, .., 3) => 42,
|
||||
| ^^
|
||||
help: consider refactoring into `(1, .., 3) | (.., 3)`
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms.rs:17:9
|
||||
|
|
||||
LL | (1, .., 3) => 42,
|
||||
| ^^^^^^^^^^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
| ----------^^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `(1, .., 3) | (.., 3)`
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms.rs:18:9
|
||||
|
|
||||
LL | (.., 3) => 42, //~ ERROR match arms have same body
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms.rs:24:15
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms.rs:24:9
|
||||
|
|
||||
LL | 51 => 1, //~ ERROR match arms have same body
|
||||
| ^
|
||||
| --^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `51 | 42`
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms.rs:23:15
|
||||
|
|
||||
LL | 42 => 1,
|
||||
| ^
|
||||
help: consider refactoring into `42 | 51`
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms.rs:23:9
|
||||
|
|
||||
LL | 42 => 1,
|
||||
| ^^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
| ^^^^^^^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms.rs:26:15
|
||||
|
|
||||
LL | 52 => 2, //~ ERROR match arms have same body
|
||||
| ^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms.rs:25:15
|
||||
|
|
||||
LL | 41 => 2,
|
||||
| ^
|
||||
help: consider refactoring into `41 | 52`
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms.rs:25:9
|
||||
|
|
||||
LL | 41 => 2,
|
||||
| ^^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
| --^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `41 | 52`
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms.rs:26:9
|
||||
|
|
||||
LL | 52 => 2, //~ ERROR match arms have same body
|
||||
| ^^^^^^^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms.rs:32:14
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms.rs:32:9
|
||||
|
|
||||
LL | 2 => 2, //~ ERROR 2nd matched arms have same body
|
||||
| ^
|
||||
| -^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `2 | 1`
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms.rs:31:14
|
||||
|
|
||||
LL | 1 => 2,
|
||||
| ^
|
||||
help: consider refactoring into `1 | 2`
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms.rs:31:9
|
||||
|
|
||||
LL | 1 => 2,
|
||||
| ^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
| ^^^^^^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms.rs:33:14
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms.rs:33:9
|
||||
|
|
||||
LL | 3 => 2, //~ ERROR 3rd matched arms have same body
|
||||
| ^
|
||||
| -^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `3 | 1`
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms.rs:31:14
|
||||
|
|
||||
LL | 1 => 2,
|
||||
| ^
|
||||
help: consider refactoring into `1 | 3`
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms.rs:31:9
|
||||
|
|
||||
LL | 1 => 2,
|
||||
| ^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
| ^^^^^^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms.rs:50:55
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms.rs:32:9
|
||||
|
|
||||
LL | 2 => 2, //~ ERROR 2nd matched arms have same body
|
||||
| -^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `2 | 3`
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms.rs:33:9
|
||||
|
|
||||
LL | 3 => 2, //~ ERROR 3rd matched arms have same body
|
||||
| ^^^^^^
|
||||
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms.rs:50:17
|
||||
|
|
||||
LL | CommandInfo::External { name, .. } => name.to_string(),
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
| ----------------------------------^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. }`
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms.rs:49:54
|
||||
|
|
||||
LL | CommandInfo::BuiltIn { name, .. } => name.to_string(),
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
help: consider refactoring into `CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. }`
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms.rs:49:17
|
||||
|
|
||||
LL | CommandInfo::BuiltIn { name, .. } => name.to_string(),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
|
|
|
@ -174,4 +174,57 @@ fn main() {
|
|||
Some(2) => 2,
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
enum Foo {
|
||||
X(u32),
|
||||
Y(u32),
|
||||
Z(u32),
|
||||
}
|
||||
|
||||
// Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between.
|
||||
let _ = match Foo::X(0) {
|
||||
Foo::X(0) => 1,
|
||||
Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2,
|
||||
Foo::Z(_) => 1,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
// Suggest moving `Foo::Z(_)` up.
|
||||
let _ = match Foo::X(0) {
|
||||
Foo::X(0) => 1,
|
||||
Foo::X(_) | Foo::Y(_) => 2,
|
||||
Foo::Z(_) => 1,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
// Suggest moving `Foo::X(0)` down.
|
||||
let _ = match Foo::X(0) {
|
||||
Foo::X(0) => 1,
|
||||
Foo::Y(_) | Foo::Z(0) => 2,
|
||||
Foo::Z(_) => 1,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
// Don't lint.
|
||||
let _ = match 0 {
|
||||
-2 => 1,
|
||||
-5..=50 => 2,
|
||||
-150..=88 => 1,
|
||||
_ => 3,
|
||||
};
|
||||
|
||||
struct Bar {
|
||||
x: u32,
|
||||
y: u32,
|
||||
z: u32,
|
||||
}
|
||||
|
||||
// Lint.
|
||||
let _ = match None {
|
||||
Some(Bar { x: 0, y: 5, .. }) => 1,
|
||||
Some(Bar { y: 10, z: 0, .. }) => 2,
|
||||
None => 50,
|
||||
Some(Bar { y: 0, x: 5, .. }) => 1,
|
||||
_ => 200,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,175 +1,138 @@
|
|||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms2.rs:20:14
|
||||
error: this match arm has an identical body to the `_` wildcard arm
|
||||
--> $DIR/match_same_arms2.rs:11:9
|
||||
|
|
||||
LL | _ => {
|
||||
| ______________^
|
||||
LL | / 42 => {
|
||||
LL | | foo();
|
||||
LL | | let mut a = 42 + [23].len() as i32;
|
||||
LL | | if true {
|
||||
... |
|
||||
LL | | a
|
||||
LL | | },
|
||||
| |_________^ help: try removing the arm
|
||||
|
|
||||
= note: `-D clippy::match-same-arms` implied by `-D warnings`
|
||||
= help: or try changing either arm body
|
||||
note: `_` wildcard arm here
|
||||
--> $DIR/match_same_arms2.rs:20:9
|
||||
|
|
||||
LL | / _ => {
|
||||
LL | | //~ ERROR match arms have same body
|
||||
LL | | foo();
|
||||
LL | | let mut a = 42 + [23].len() as i32;
|
||||
... |
|
||||
LL | | a
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
|
||||
= note: `-D clippy::match-same-arms` implied by `-D warnings`
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms2.rs:11:15
|
||||
|
|
||||
LL | 42 => {
|
||||
| _______________^
|
||||
LL | | foo();
|
||||
LL | | let mut a = 42 + [23].len() as i32;
|
||||
LL | | if true {
|
||||
... |
|
||||
LL | | a
|
||||
LL | | },
|
||||
| |_________^
|
||||
note: `42` has the same arm body as the `_` wildcard, consider removing it
|
||||
--> $DIR/match_same_arms2.rs:11:15
|
||||
|
|
||||
LL | 42 => {
|
||||
| _______________^
|
||||
LL | | foo();
|
||||
LL | | let mut a = 42 + [23].len() as i32;
|
||||
LL | | if true {
|
||||
... |
|
||||
LL | | a
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms2.rs:34:15
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms2.rs:34:9
|
||||
|
|
||||
LL | 51 => foo(), //~ ERROR match arms have same body
|
||||
| ^^^^^
|
||||
| --^^^^^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `51 | 42`
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms2.rs:33:15
|
||||
|
|
||||
LL | 42 => foo(),
|
||||
| ^^^^^
|
||||
help: consider refactoring into `42 | 51`
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms2.rs:33:9
|
||||
|
|
||||
LL | 42 => foo(),
|
||||
| ^^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms2.rs:40:17
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms2.rs:40:9
|
||||
|
|
||||
LL | None => 24, //~ ERROR match arms have same body
|
||||
| ^^
|
||||
| ----^^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `None | Some(_)`
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms2.rs:39:20
|
||||
|
|
||||
LL | Some(_) => 24,
|
||||
| ^^
|
||||
help: consider refactoring into `Some(_) | None`
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms2.rs:39:9
|
||||
|
|
||||
LL | Some(_) => 24,
|
||||
| ^^^^^^^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms2.rs:62:28
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms2.rs:62:9
|
||||
|
|
||||
LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body
|
||||
| ^^^^^^
|
||||
| ---------------^^^^^^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)`
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms2.rs:61:28
|
||||
|
|
||||
LL | (Some(a), None) => bar(a),
|
||||
| ^^^^^^
|
||||
help: consider refactoring into `(Some(a), None) | (None, Some(a))`
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms2.rs:61:9
|
||||
|
|
||||
LL | (Some(a), None) => bar(a),
|
||||
| ^^^^^^^^^^^^^^^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms2.rs:68:26
|
||||
|
|
||||
LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body
|
||||
| ^^^^^^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms2.rs:67:26
|
||||
|
|
||||
LL | (Some(a), ..) => bar(a),
|
||||
| ^^^^^^
|
||||
help: consider refactoring into `(Some(a), ..) | (.., Some(a))`
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms2.rs:67:9
|
||||
|
|
||||
LL | (Some(a), ..) => bar(a),
|
||||
| ^^^^^^^^^^^^^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
| -------------^^^^^^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `(Some(a), ..) | (.., Some(a))`
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms2.rs:68:9
|
||||
|
|
||||
LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms2.rs:102:29
|
||||
|
|
||||
LL | (Ok(_), Some(x)) => println!("ok {}", x),
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms2.rs:101:29
|
||||
|
|
||||
LL | (Ok(x), Some(_)) => println!("ok {}", x),
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
help: consider refactoring into `(Ok(x), Some(_)) | (Ok(_), Some(x))`
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms2.rs:101:9
|
||||
|
|
||||
LL | (Ok(x), Some(_)) => println!("ok {}", x),
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
| ----------------^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `(Ok(x), Some(_)) | (Ok(_), Some(x))`
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms2.rs:102:9
|
||||
|
|
||||
LL | (Ok(_), Some(x)) => println!("ok {}", x),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms2.rs:117:18
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms2.rs:117:9
|
||||
|
|
||||
LL | Ok(_) => println!("ok"),
|
||||
| ^^^^^^^^^^^^^^
|
||||
| -----^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `Ok(_) | Ok(3)`
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms2.rs:116:18
|
||||
|
|
||||
LL | Ok(3) => println!("ok"),
|
||||
| ^^^^^^^^^^^^^^
|
||||
help: consider refactoring into `Ok(3) | Ok(_)`
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms2.rs:116:9
|
||||
|
|
||||
LL | Ok(3) => println!("ok"),
|
||||
| ^^^^^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this `match` has identical arm bodies
|
||||
--> $DIR/match_same_arms2.rs:144:14
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms2.rs:144:9
|
||||
|
|
||||
LL | 1 => {
|
||||
| ______________^
|
||||
| ^ help: try merging the arm patterns: `1 | 0`
|
||||
| _________|
|
||||
| |
|
||||
LL | | empty!(0);
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/match_same_arms2.rs:141:14
|
||||
|
|
||||
LL | 0 => {
|
||||
| ______________^
|
||||
LL | | empty!(0);
|
||||
LL | | },
|
||||
| |_________^
|
||||
help: consider refactoring into `0 | 1`
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms2.rs:141:9
|
||||
|
|
||||
LL | 0 => {
|
||||
| ^
|
||||
= help: ...or consider changing the match arm bodies
|
||||
LL | / 0 => {
|
||||
LL | | empty!(0);
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
||||
error: match expression looks like `matches!` macro
|
||||
--> $DIR/match_same_arms2.rs:162:16
|
||||
|
@ -184,5 +147,50 @@ LL | | };
|
|||
|
|
||||
= note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms2.rs:194:9
|
||||
|
|
||||
LL | Foo::X(0) => 1,
|
||||
| ---------^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `Foo::X(0) | Foo::Z(_)`
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms2.rs:196:9
|
||||
|
|
||||
LL | Foo::Z(_) => 1,
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms2.rs:204:9
|
||||
|
|
||||
LL | Foo::Z(_) => 1,
|
||||
| ---------^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `Foo::Z(_) | Foo::X(0)`
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms2.rs:202:9
|
||||
|
|
||||
LL | Foo::X(0) => 1,
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: this match arm has an identical body to another arm
|
||||
--> $DIR/match_same_arms2.rs:227:9
|
||||
|
|
||||
LL | Some(Bar { y: 0, x: 5, .. }) => 1,
|
||||
| ----------------------------^^^^^
|
||||
| |
|
||||
| help: try merging the arm patterns: `Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. })`
|
||||
|
|
||||
= help: or try changing either arm body
|
||||
note: other arm here
|
||||
--> $DIR/match_same_arms2.rs:224:9
|
||||
|
|
||||
LL | Some(Bar { x: 0, y: 5, .. }) => 1,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
|
|
52
tests/ui/or_then_unwrap.fixed
Normal file
52
tests/ui/or_then_unwrap.fixed
Normal file
|
@ -0,0 +1,52 @@
|
|||
// run-rustfix
|
||||
|
||||
#![warn(clippy::or_then_unwrap)]
|
||||
#![allow(clippy::map_identity)]
|
||||
|
||||
struct SomeStruct {}
|
||||
impl SomeStruct {
|
||||
fn or(self, _: Option<Self>) -> Self {
|
||||
self
|
||||
}
|
||||
fn unwrap(&self) {}
|
||||
}
|
||||
|
||||
struct SomeOtherStruct {}
|
||||
impl SomeOtherStruct {
|
||||
fn or(self) -> Self {
|
||||
self
|
||||
}
|
||||
fn unwrap(&self) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let option: Option<&str> = None;
|
||||
let _ = option.unwrap_or("fallback"); // should trigger lint
|
||||
|
||||
let result: Result<&str, &str> = Err("Error");
|
||||
let _ = result.unwrap_or("fallback"); // should trigger lint
|
||||
|
||||
// as part of a method chain
|
||||
let option: Option<&str> = None;
|
||||
let _ = option.map(|v| v).unwrap_or("fallback").to_string().chars(); // should trigger lint
|
||||
|
||||
// Not Option/Result
|
||||
let instance = SomeStruct {};
|
||||
let _ = instance.or(Some(SomeStruct {})).unwrap(); // should not trigger lint
|
||||
|
||||
// or takes no argument
|
||||
let instance = SomeOtherStruct {};
|
||||
let _ = instance.or().unwrap(); // should not trigger lint and should not panic
|
||||
|
||||
// None in or
|
||||
let option: Option<&str> = None;
|
||||
let _ = option.or(None).unwrap(); // should not trigger lint
|
||||
|
||||
// Not Err in or
|
||||
let result: Result<&str, &str> = Err("Error");
|
||||
let _ = result.or::<&str>(Err("Other Error")).unwrap(); // should not trigger lint
|
||||
|
||||
// other function between
|
||||
let option: Option<&str> = None;
|
||||
let _ = option.or(Some("fallback")).map(|v| v).unwrap(); // should not trigger lint
|
||||
}
|
52
tests/ui/or_then_unwrap.rs
Normal file
52
tests/ui/or_then_unwrap.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
// run-rustfix
|
||||
|
||||
#![warn(clippy::or_then_unwrap)]
|
||||
#![allow(clippy::map_identity)]
|
||||
|
||||
struct SomeStruct {}
|
||||
impl SomeStruct {
|
||||
fn or(self, _: Option<Self>) -> Self {
|
||||
self
|
||||
}
|
||||
fn unwrap(&self) {}
|
||||
}
|
||||
|
||||
struct SomeOtherStruct {}
|
||||
impl SomeOtherStruct {
|
||||
fn or(self) -> Self {
|
||||
self
|
||||
}
|
||||
fn unwrap(&self) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let option: Option<&str> = None;
|
||||
let _ = option.or(Some("fallback")).unwrap(); // should trigger lint
|
||||
|
||||
let result: Result<&str, &str> = Err("Error");
|
||||
let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger lint
|
||||
|
||||
// as part of a method chain
|
||||
let option: Option<&str> = None;
|
||||
let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint
|
||||
|
||||
// Not Option/Result
|
||||
let instance = SomeStruct {};
|
||||
let _ = instance.or(Some(SomeStruct {})).unwrap(); // should not trigger lint
|
||||
|
||||
// or takes no argument
|
||||
let instance = SomeOtherStruct {};
|
||||
let _ = instance.or().unwrap(); // should not trigger lint and should not panic
|
||||
|
||||
// None in or
|
||||
let option: Option<&str> = None;
|
||||
let _ = option.or(None).unwrap(); // should not trigger lint
|
||||
|
||||
// Not Err in or
|
||||
let result: Result<&str, &str> = Err("Error");
|
||||
let _ = result.or::<&str>(Err("Other Error")).unwrap(); // should not trigger lint
|
||||
|
||||
// other function between
|
||||
let option: Option<&str> = None;
|
||||
let _ = option.or(Some("fallback")).map(|v| v).unwrap(); // should not trigger lint
|
||||
}
|
22
tests/ui/or_then_unwrap.stderr
Normal file
22
tests/ui/or_then_unwrap.stderr
Normal file
|
@ -0,0 +1,22 @@
|
|||
error: found `.or(Some(…)).unwrap()`
|
||||
--> $DIR/or_then_unwrap.rs:24:20
|
||||
|
|
||||
LL | let _ = option.or(Some("fallback")).unwrap(); // should trigger lint
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")`
|
||||
|
|
||||
= note: `-D clippy::or-then-unwrap` implied by `-D warnings`
|
||||
|
||||
error: found `.or(Ok(…)).unwrap()`
|
||||
--> $DIR/or_then_unwrap.rs:27:20
|
||||
|
|
||||
LL | let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger lint
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")`
|
||||
|
||||
error: found `.or(Some(…)).unwrap()`
|
||||
--> $DIR/or_then_unwrap.rs:31:31
|
||||
|
|
||||
LL | let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or("fallback")`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
|
@ -194,3 +194,10 @@ fn two_vecs(a: &mut Vec<u32>, b: &mut Vec<u32>) {
|
|||
a.push(0);
|
||||
b.push(1);
|
||||
}
|
||||
|
||||
// Issue #8495
|
||||
fn cow_conditional_to_mut(a: &mut Cow<str>) {
|
||||
if a.is_empty() {
|
||||
a.to_mut().push_str("foo");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
// run-rustfix
|
||||
#![warn(clippy::single_component_path_imports)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
// #7106: use statements exporting a macro within a crate should not trigger lint
|
||||
|
||||
macro_rules! m1 {
|
||||
() => {};
|
||||
}
|
||||
pub(crate) use m1; // ok
|
||||
|
||||
macro_rules! m2 {
|
||||
() => {};
|
||||
}
|
||||
// fail
|
||||
|
||||
fn main() {
|
||||
m1!();
|
||||
m2!();
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
// run-rustfix
|
||||
#![warn(clippy::single_component_path_imports)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
// #7106: use statements exporting a macro within a crate should not trigger lint
|
||||
// #7923: normal `use` statements of macros should also not trigger the lint
|
||||
|
||||
macro_rules! m1 {
|
||||
() => {};
|
||||
|
@ -12,7 +12,7 @@ pub(crate) use m1; // ok
|
|||
macro_rules! m2 {
|
||||
() => {};
|
||||
}
|
||||
use m2; // fail
|
||||
use m2; // ok
|
||||
|
||||
fn main() {
|
||||
m1!();
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
error: this import is redundant
|
||||
--> $DIR/single_component_path_imports_macro.rs:15:1
|
||||
|
|
||||
LL | use m2; // fail
|
||||
| ^^^^^^^ help: remove it entirely
|
||||
|
|
||||
= note: `-D clippy::single-component-path-imports` implied by `-D warnings`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
#![warn(clippy::transmute_undefined_repr)]
|
||||
#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)]
|
||||
|
||||
use core::any::TypeId;
|
||||
use core::ffi::c_void;
|
||||
use core::mem::{size_of, transmute};
|
||||
use core::mem::{size_of, transmute, MaybeUninit};
|
||||
|
||||
fn value<T>() -> T {
|
||||
unimplemented!()
|
||||
|
@ -87,5 +88,57 @@ fn main() {
|
|||
|
||||
let _: *const [u8] = transmute(value::<Box<[u8]>>()); // Ok
|
||||
let _: Box<[u8]> = transmute(value::<*mut [u8]>()); // Ok
|
||||
|
||||
let _: Ty2<u32, u32> = transmute(value::<(Ty2<u32, u32>,)>()); // Ok
|
||||
let _: (Ty2<u32, u32>,) = transmute(value::<Ty2<u32, u32>>()); // Ok
|
||||
|
||||
let _: Ty2<u32, u32> = transmute(value::<(Ty2<u32, u32>, ())>()); // Ok
|
||||
let _: (Ty2<u32, u32>, ()) = transmute(value::<Ty2<u32, u32>>()); // Ok
|
||||
|
||||
let _: Ty2<u32, u32> = transmute(value::<((), Ty2<u32, u32>)>()); // Ok
|
||||
let _: ((), Ty2<u32, u32>) = transmute(value::<Ty2<u32, u32>>()); // Ok
|
||||
|
||||
let _: (usize, usize) = transmute(value::<&[u8]>()); // Ok
|
||||
let _: &[u8] = transmute(value::<(usize, usize)>()); // Ok
|
||||
|
||||
trait Trait {}
|
||||
let _: (isize, isize) = transmute(value::<&dyn Trait>()); // Ok
|
||||
let _: &dyn Trait = transmute(value::<(isize, isize)>()); // Ok
|
||||
|
||||
let _: MaybeUninit<Ty2<u32, u32>> = transmute(value::<Ty2<u32, u32>>()); // Ok
|
||||
let _: Ty2<u32, u32> = transmute(value::<MaybeUninit<Ty2<u32, u32>>>()); // Ok
|
||||
|
||||
let _: Ty<&[u32]> = transmute::<&[u32], _>(value::<&Vec<u32>>()); // Ok
|
||||
}
|
||||
}
|
||||
|
||||
fn _with_generics<T: 'static, U: 'static>() {
|
||||
if TypeId::of::<T>() != TypeId::of::<u32>() || TypeId::of::<T>() != TypeId::of::<U>() {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
let _: &u32 = transmute(value::<&T>()); // Ok
|
||||
let _: &T = transmute(value::<&u32>()); // Ok
|
||||
|
||||
let _: Vec<U> = transmute(value::<Vec<T>>()); // Ok
|
||||
let _: Vec<T> = transmute(value::<Vec<U>>()); // Ok
|
||||
|
||||
let _: Ty<&u32> = transmute(value::<&T>()); // Ok
|
||||
let _: Ty<&T> = transmute(value::<&u32>()); // Ok
|
||||
|
||||
let _: Vec<u32> = transmute(value::<Vec<T>>()); // Ok
|
||||
let _: Vec<T> = transmute(value::<Vec<u32>>()); // Ok
|
||||
|
||||
let _: &Ty2<u32, u32> = transmute(value::<&Ty2<T, U>>()); // Ok
|
||||
let _: &Ty2<T, U> = transmute(value::<&Ty2<u32, u32>>()); // Ok
|
||||
|
||||
let _: Vec<Vec<u32>> = transmute(value::<Vec<Vec<T>>>()); // Ok
|
||||
let _: Vec<Vec<T>> = transmute(value::<Vec<Vec<u32>>>()); // Ok
|
||||
|
||||
let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
|
||||
let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err
|
||||
|
||||
let _: *const u32 = transmute(value::<Box<T>>()); // Ok
|
||||
let _: Box<T> = transmute(value::<*const u32>()); // Ok
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: transmute from `Ty2<u32, i32>` which has an undefined layout
|
||||
--> $DIR/transmute_undefined_repr.rs:26:33
|
||||
--> $DIR/transmute_undefined_repr.rs:27:33
|
||||
|
|
||||
LL | let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -7,13 +7,13 @@ LL | let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lin
|
|||
= note: `-D clippy::transmute-undefined-repr` implied by `-D warnings`
|
||||
|
||||
error: transmute into `Ty2<u32, i32>` which has an undefined layout
|
||||
--> $DIR/transmute_undefined_repr.rs:27:32
|
||||
--> $DIR/transmute_undefined_repr.rs:28:32
|
||||
|
|
||||
LL | let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: transmute from `Ty<Ty2<u32, i32>>` to `Ty2<u32, f32>`, both of which have an undefined layout
|
||||
--> $DIR/transmute_undefined_repr.rs:32:32
|
||||
--> $DIR/transmute_undefined_repr.rs:33:32
|
||||
|
|
||||
LL | let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -21,7 +21,7 @@ LL | let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); //
|
|||
= note: two instances of the same generic type (`Ty2`) may have different layouts
|
||||
|
||||
error: transmute from `Ty2<u32, f32>` to `Ty<Ty2<u32, i32>>`, both of which have an undefined layout
|
||||
--> $DIR/transmute_undefined_repr.rs:33:36
|
||||
--> $DIR/transmute_undefined_repr.rs:34:36
|
||||
|
|
||||
LL | let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -29,7 +29,7 @@ LL | let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); //
|
|||
= note: two instances of the same generic type (`Ty2`) may have different layouts
|
||||
|
||||
error: transmute from `Ty<&Ty2<u32, i32>>` to `&Ty2<u32, f32>`, both of which have an undefined layout
|
||||
--> $DIR/transmute_undefined_repr.rs:38:33
|
||||
--> $DIR/transmute_undefined_repr.rs:39:33
|
||||
|
|
||||
LL | let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -37,7 +37,7 @@ LL | let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); /
|
|||
= note: two instances of the same generic type (`Ty2`) may have different layouts
|
||||
|
||||
error: transmute from `&Ty2<u32, f32>` to `Ty<&Ty2<u32, i32>>`, both of which have an undefined layout
|
||||
--> $DIR/transmute_undefined_repr.rs:39:37
|
||||
--> $DIR/transmute_undefined_repr.rs:40:37
|
||||
|
|
||||
LL | let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -45,7 +45,7 @@ LL | let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); /
|
|||
= note: two instances of the same generic type (`Ty2`) may have different layouts
|
||||
|
||||
error: transmute from `std::boxed::Box<Ty2<u32, u32>>` to `&mut Ty2<u32, f32>`, both of which have an undefined layout
|
||||
--> $DIR/transmute_undefined_repr.rs:56:45
|
||||
--> $DIR/transmute_undefined_repr.rs:57:45
|
||||
|
|
||||
LL | let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -53,12 +53,28 @@ LL | let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32,
|
|||
= note: two instances of the same generic type (`Ty2`) may have different layouts
|
||||
|
||||
error: transmute from `&mut Ty2<u32, f32>` to `std::boxed::Box<Ty2<u32, u32>>`, both of which have an undefined layout
|
||||
--> $DIR/transmute_undefined_repr.rs:57:37
|
||||
--> $DIR/transmute_undefined_repr.rs:58:37
|
||||
|
|
||||
LL | let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: two instances of the same generic type (`Ty2`) may have different layouts
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error: transmute from `std::vec::Vec<Ty2<U, i32>>` to `std::vec::Vec<Ty2<T, u32>>`, both of which have an undefined layout
|
||||
--> $DIR/transmute_undefined_repr.rs:138:35
|
||||
|
|
||||
LL | let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: two instances of the same generic type (`Vec`) may have different layouts
|
||||
|
||||
error: transmute from `std::vec::Vec<Ty2<T, u32>>` to `std::vec::Vec<Ty2<U, i32>>`, both of which have an undefined layout
|
||||
--> $DIR/transmute_undefined_repr.rs:139:35
|
||||
|
|
||||
LL | let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: two instances of the same generic type (`Vec`) may have different layouts
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
|
|
|
@ -34,13 +34,13 @@ error: transmute from a reference to a pointer
|
|||
LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]`
|
||||
|
||||
error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead
|
||||
error: transmute from `fn(usize) -> u8` to `*const usize` which could be expressed as a pointer cast instead
|
||||
--> $DIR/transmutes_expressible_as_ptr_casts.rs:48:41
|
||||
|
|
||||
LL | let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize`
|
||||
|
||||
error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead
|
||||
error: transmute from `fn(usize) -> u8` to `usize` which could be expressed as a pointer cast instead
|
||||
--> $DIR/transmutes_expressible_as_ptr_casts.rs:52:49
|
||||
|
|
||||
LL | let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
|
||||
|
|
35
tests/ui/unnecessary_join.fixed
Normal file
35
tests/ui/unnecessary_join.fixed
Normal file
|
@ -0,0 +1,35 @@
|
|||
// run-rustfix
|
||||
|
||||
#![warn(clippy::unnecessary_join)]
|
||||
|
||||
fn main() {
|
||||
// should be linted
|
||||
let vector = vec!["hello", "world"];
|
||||
let output = vector
|
||||
.iter()
|
||||
.map(|item| item.to_uppercase())
|
||||
.collect::<String>();
|
||||
println!("{}", output);
|
||||
|
||||
// should be linted
|
||||
let vector = vec!["hello", "world"];
|
||||
let output = vector
|
||||
.iter()
|
||||
.map(|item| item.to_uppercase())
|
||||
.collect::<String>();
|
||||
println!("{}", output);
|
||||
|
||||
// should not be linted
|
||||
let vector = vec!["hello", "world"];
|
||||
let output = vector
|
||||
.iter()
|
||||
.map(|item| item.to_uppercase())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
println!("{}", output);
|
||||
|
||||
// should not be linted
|
||||
let vector = vec!["hello", "world"];
|
||||
let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
|
||||
println!("{}", output);
|
||||
}
|
37
tests/ui/unnecessary_join.rs
Normal file
37
tests/ui/unnecessary_join.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
// run-rustfix
|
||||
|
||||
#![warn(clippy::unnecessary_join)]
|
||||
|
||||
fn main() {
|
||||
// should be linted
|
||||
let vector = vec!["hello", "world"];
|
||||
let output = vector
|
||||
.iter()
|
||||
.map(|item| item.to_uppercase())
|
||||
.collect::<Vec<String>>()
|
||||
.join("");
|
||||
println!("{}", output);
|
||||
|
||||
// should be linted
|
||||
let vector = vec!["hello", "world"];
|
||||
let output = vector
|
||||
.iter()
|
||||
.map(|item| item.to_uppercase())
|
||||
.collect::<Vec<_>>()
|
||||
.join("");
|
||||
println!("{}", output);
|
||||
|
||||
// should not be linted
|
||||
let vector = vec!["hello", "world"];
|
||||
let output = vector
|
||||
.iter()
|
||||
.map(|item| item.to_uppercase())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
println!("{}", output);
|
||||
|
||||
// should not be linted
|
||||
let vector = vec!["hello", "world"];
|
||||
let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
|
||||
println!("{}", output);
|
||||
}
|
20
tests/ui/unnecessary_join.stderr
Normal file
20
tests/ui/unnecessary_join.stderr
Normal file
|
@ -0,0 +1,20 @@
|
|||
error: called `.collect<Vec<String>>().join("")` on an iterator
|
||||
--> $DIR/unnecessary_join.rs:11:10
|
||||
|
|
||||
LL | .collect::<Vec<String>>()
|
||||
| __________^
|
||||
LL | | .join("");
|
||||
| |_________________^ help: try using: `collect::<String>()`
|
||||
|
|
||||
= note: `-D clippy::unnecessary-join` implied by `-D warnings`
|
||||
|
||||
error: called `.collect<Vec<String>>().join("")` on an iterator
|
||||
--> $DIR/unnecessary_join.rs:20:10
|
||||
|
|
||||
LL | .collect::<Vec<_>>()
|
||||
| __________^
|
||||
LL | | .join("");
|
||||
| |_________________^ help: try using: `collect::<String>()`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -115,6 +115,14 @@ fn main() {
|
|||
let _: Result<usize, usize> = res.or(Ok(2));
|
||||
let _: Result<usize, usize> = res.or(Ok(astronomers_pi));
|
||||
let _: Result<usize, usize> = res.or(Ok(ext_str.some_field));
|
||||
let _: Result<usize, usize> = res.
|
||||
// some lines
|
||||
// some lines
|
||||
// some lines
|
||||
// some lines
|
||||
// some lines
|
||||
// some lines
|
||||
or(Ok(ext_str.some_field));
|
||||
|
||||
// neither bind_instead_of_map nor unnecessary_lazy_eval applies here
|
||||
let _: Result<usize, usize> = res.and_then(|x| Err(x));
|
||||
|
|
|
@ -115,6 +115,14 @@ fn main() {
|
|||
let _: Result<usize, usize> = res.or_else(|_| Ok(2));
|
||||
let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
|
||||
let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
|
||||
let _: Result<usize, usize> = res.
|
||||
// some lines
|
||||
// some lines
|
||||
// some lines
|
||||
// some lines
|
||||
// some lines
|
||||
// some lines
|
||||
or_else(|_| Ok(ext_str.some_field));
|
||||
|
||||
// neither bind_instead_of_map nor unnecessary_lazy_eval applies here
|
||||
let _: Result<usize, usize> = res.and_then(|x| Err(x));
|
||||
|
|
|
@ -2,7 +2,9 @@ error: unnecessary closure used to substitute value for `Option::None`
|
|||
--> $DIR/unnecessary_lazy_eval.rs:35:13
|
||||
|
|
||||
LL | let _ = opt.unwrap_or_else(|| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(2)`
|
||||
| ^^^^--------------------
|
||||
| |
|
||||
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
|
||||
|
|
||||
= note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
|
||||
|
||||
|
@ -10,187 +12,264 @@ error: unnecessary closure used to substitute value for `Option::None`
|
|||
--> $DIR/unnecessary_lazy_eval.rs:36:13
|
||||
|
|
||||
LL | let _ = opt.unwrap_or_else(|| astronomers_pi);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)`
|
||||
| ^^^^---------------------------------
|
||||
| |
|
||||
| help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:37:13
|
||||
|
|
||||
LL | let _ = opt.unwrap_or_else(|| ext_str.some_field);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)`
|
||||
| ^^^^-------------------------------------
|
||||
| |
|
||||
| help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:39:13
|
||||
|
|
||||
LL | let _ = opt.and_then(|_| ext_opt);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `opt.and(ext_opt)`
|
||||
| ^^^^---------------------
|
||||
| |
|
||||
| help: use `and(..)` instead: `and(ext_opt)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:40:13
|
||||
|
|
||||
LL | let _ = opt.or_else(|| ext_opt);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(ext_opt)`
|
||||
| ^^^^-------------------
|
||||
| |
|
||||
| help: use `or(..)` instead: `or(ext_opt)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:41:13
|
||||
|
|
||||
LL | let _ = opt.or_else(|| None);
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(None)`
|
||||
| ^^^^----------------
|
||||
| |
|
||||
| help: use `or(..)` instead: `or(None)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:42:13
|
||||
|
|
||||
LL | let _ = opt.get_or_insert_with(|| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `opt.get_or_insert(2)`
|
||||
| ^^^^------------------------
|
||||
| |
|
||||
| help: use `get_or_insert(..)` instead: `get_or_insert(2)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:43:13
|
||||
|
|
||||
LL | let _ = opt.ok_or_else(|| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `opt.ok_or(2)`
|
||||
| ^^^^----------------
|
||||
| |
|
||||
| help: use `ok_or(..)` instead: `ok_or(2)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:44:13
|
||||
|
|
||||
LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `nested_tuple_opt.unwrap_or(Some((1, 2)))`
|
||||
| ^^^^^^^^^^^^^^^^^-------------------------------
|
||||
| |
|
||||
| help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:47:13
|
||||
|
|
||||
LL | let _ = Some(10).unwrap_or_else(|| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Some(10).unwrap_or(2)`
|
||||
| ^^^^^^^^^--------------------
|
||||
| |
|
||||
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:48:13
|
||||
|
|
||||
LL | let _ = Some(10).and_then(|_| ext_opt);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `Some(10).and(ext_opt)`
|
||||
| ^^^^^^^^^---------------------
|
||||
| |
|
||||
| help: use `and(..)` instead: `and(ext_opt)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:49:28
|
||||
|
|
||||
LL | let _: Option<usize> = None.or_else(|| ext_opt);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(ext_opt)`
|
||||
| ^^^^^-------------------
|
||||
| |
|
||||
| help: use `or(..)` instead: `or(ext_opt)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:50:13
|
||||
|
|
||||
LL | let _ = None.get_or_insert_with(|| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `None.get_or_insert(2)`
|
||||
| ^^^^^------------------------
|
||||
| |
|
||||
| help: use `get_or_insert(..)` instead: `get_or_insert(2)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:51:35
|
||||
|
|
||||
LL | let _: Result<usize, usize> = None.ok_or_else(|| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `None.ok_or(2)`
|
||||
| ^^^^^----------------
|
||||
| |
|
||||
| help: use `ok_or(..)` instead: `ok_or(2)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:52:28
|
||||
|
|
||||
LL | let _: Option<usize> = None.or_else(|| None);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(None)`
|
||||
| ^^^^^----------------
|
||||
| |
|
||||
| help: use `or(..)` instead: `or(None)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:55:13
|
||||
|
|
||||
LL | let _ = deep.0.unwrap_or_else(|| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `deep.0.unwrap_or(2)`
|
||||
| ^^^^^^^--------------------
|
||||
| |
|
||||
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:56:13
|
||||
|
|
||||
LL | let _ = deep.0.and_then(|_| ext_opt);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `deep.0.and(ext_opt)`
|
||||
| ^^^^^^^---------------------
|
||||
| |
|
||||
| help: use `and(..)` instead: `and(ext_opt)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:57:13
|
||||
|
|
||||
LL | let _ = deep.0.or_else(|| None);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `deep.0.or(None)`
|
||||
| ^^^^^^^----------------
|
||||
| |
|
||||
| help: use `or(..)` instead: `or(None)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:58:13
|
||||
|
|
||||
LL | let _ = deep.0.get_or_insert_with(|| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `deep.0.get_or_insert(2)`
|
||||
| ^^^^^^^------------------------
|
||||
| |
|
||||
| help: use `get_or_insert(..)` instead: `get_or_insert(2)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:59:13
|
||||
|
|
||||
LL | let _ = deep.0.ok_or_else(|| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `deep.0.ok_or(2)`
|
||||
| ^^^^^^^----------------
|
||||
| |
|
||||
| help: use `ok_or(..)` instead: `ok_or(2)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:79:28
|
||||
|
|
||||
LL | let _: Option<usize> = None.or_else(|| Some(3));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(Some(3))`
|
||||
| ^^^^^-------------------
|
||||
| |
|
||||
| help: use `or(..)` instead: `or(Some(3))`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:80:13
|
||||
|
|
||||
LL | let _ = deep.0.or_else(|| Some(3));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `deep.0.or(Some(3))`
|
||||
| ^^^^^^^-------------------
|
||||
| |
|
||||
| help: use `or(..)` instead: `or(Some(3))`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Option::None`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:81:13
|
||||
|
|
||||
LL | let _ = opt.or_else(|| Some(3));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(Some(3))`
|
||||
| ^^^^-------------------
|
||||
| |
|
||||
| help: use `or(..)` instead: `or(Some(3))`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Result::Err`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:87:13
|
||||
|
|
||||
LL | let _ = res2.unwrap_or_else(|_| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(2)`
|
||||
| ^^^^^---------------------
|
||||
| |
|
||||
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Result::Err`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:88:13
|
||||
|
|
||||
LL | let _ = res2.unwrap_or_else(|_| astronomers_pi);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)`
|
||||
| ^^^^^----------------------------------
|
||||
| |
|
||||
| help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Result::Err`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:89:13
|
||||
|
|
||||
LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)`
|
||||
| ^^^^^--------------------------------------
|
||||
| |
|
||||
| help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Result::Err`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:111:35
|
||||
|
|
||||
LL | let _: Result<usize, usize> = res.and_then(|_| Err(2));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(2))`
|
||||
| ^^^^--------------------
|
||||
| |
|
||||
| help: use `and(..)` instead: `and(Err(2))`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Result::Err`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:112:35
|
||||
|
|
||||
LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(astronomers_pi))`
|
||||
| ^^^^---------------------------------
|
||||
| |
|
||||
| help: use `and(..)` instead: `and(Err(astronomers_pi))`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Result::Err`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:113:35
|
||||
|
|
||||
LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(ext_str.some_field))`
|
||||
| ^^^^-------------------------------------
|
||||
| |
|
||||
| help: use `and(..)` instead: `and(Err(ext_str.some_field))`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Result::Err`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:115:35
|
||||
|
|
||||
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(2))`
|
||||
| ^^^^------------------
|
||||
| |
|
||||
| help: use `or(..)` instead: `or(Ok(2))`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Result::Err`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:116:35
|
||||
|
|
||||
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(astronomers_pi))`
|
||||
| ^^^^-------------------------------
|
||||
| |
|
||||
| help: use `or(..)` instead: `or(Ok(astronomers_pi))`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Result::Err`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:117:35
|
||||
|
|
||||
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(ext_str.some_field))`
|
||||
| ^^^^-----------------------------------
|
||||
| |
|
||||
| help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
|
||||
|
||||
error: aborting due to 32 previous errors
|
||||
error: unnecessary closure used to substitute value for `Result::Err`
|
||||
--> $DIR/unnecessary_lazy_eval.rs:118:35
|
||||
|
|
||||
LL | let _: Result<usize, usize> = res.
|
||||
| ___________________________________^
|
||||
LL | | // some lines
|
||||
LL | | // some lines
|
||||
LL | | // some lines
|
||||
... |
|
||||
LL | | // some lines
|
||||
LL | | or_else(|_| Ok(ext_str.some_field));
|
||||
| |_________----------------------------------^
|
||||
| |
|
||||
| help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
|
||||
|
||||
error: aborting due to 33 previous errors
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@ error: unnecessary closure used to substitute value for `Result::Err`
|
|||
--> $DIR/unnecessary_lazy_eval_unfixable.rs:12:13
|
||||
|
|
||||
LL | let _ = Ok(1).unwrap_or_else(|()| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)`
|
||||
| ^^^^^^----------------------
|
||||
| |
|
||||
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
|
||||
|
|
||||
= note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
|
||||
|
||||
|
@ -10,13 +12,17 @@ error: unnecessary closure used to substitute value for `Result::Err`
|
|||
--> $DIR/unnecessary_lazy_eval_unfixable.rs:16:13
|
||||
|
|
||||
LL | let _ = Ok(1).unwrap_or_else(|e::E| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)`
|
||||
| ^^^^^^------------------------
|
||||
| |
|
||||
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
|
||||
|
||||
error: unnecessary closure used to substitute value for `Result::Err`
|
||||
--> $DIR/unnecessary_lazy_eval_unfixable.rs:17:13
|
||||
|
|
||||
LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)`
|
||||
| ^^^^^^-------------------------------------
|
||||
| |
|
||||
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
|
|
@ -212,3 +212,51 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E
|
|||
}
|
||||
|
||||
fn require_string(_: &String) {}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/issues/8507
|
||||
mod issue_8507 {
|
||||
#![allow(dead_code)]
|
||||
|
||||
struct Opaque<P>(P);
|
||||
|
||||
pub trait Abstracted {}
|
||||
|
||||
impl<P> Abstracted for Opaque<P> {}
|
||||
|
||||
fn build<P>(p: P) -> Opaque<P>
|
||||
where
|
||||
P: AsRef<str>,
|
||||
{
|
||||
Opaque(p)
|
||||
}
|
||||
|
||||
// Should not lint.
|
||||
fn test_str(s: &str) -> Box<dyn Abstracted> {
|
||||
Box::new(build(s.to_string()))
|
||||
}
|
||||
|
||||
// Should not lint.
|
||||
fn test_x(x: super::X) -> Box<dyn Abstracted> {
|
||||
Box::new(build(x))
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Y(&'static str);
|
||||
|
||||
impl AsRef<str> for Y {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Y {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// Should lint because Y is copy.
|
||||
fn test_y(y: Y) -> Box<dyn Abstracted> {
|
||||
Box::new(build(y))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -212,3 +212,51 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E
|
|||
}
|
||||
|
||||
fn require_string(_: &String) {}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/issues/8507
|
||||
mod issue_8507 {
|
||||
#![allow(dead_code)]
|
||||
|
||||
struct Opaque<P>(P);
|
||||
|
||||
pub trait Abstracted {}
|
||||
|
||||
impl<P> Abstracted for Opaque<P> {}
|
||||
|
||||
fn build<P>(p: P) -> Opaque<P>
|
||||
where
|
||||
P: AsRef<str>,
|
||||
{
|
||||
Opaque(p)
|
||||
}
|
||||
|
||||
// Should not lint.
|
||||
fn test_str(s: &str) -> Box<dyn Abstracted> {
|
||||
Box::new(build(s.to_string()))
|
||||
}
|
||||
|
||||
// Should not lint.
|
||||
fn test_x(x: super::X) -> Box<dyn Abstracted> {
|
||||
Box::new(build(x))
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Y(&'static str);
|
||||
|
||||
impl AsRef<str> for Y {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Y {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// Should lint because Y is copy.
|
||||
fn test_y(y: Y) -> Box<dyn Abstracted> {
|
||||
Box::new(build(y.to_string()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -491,5 +491,11 @@ LL - let path = match get_file_path(&t) {
|
|||
LL + let path = match get_file_path(t) {
|
||||
|
|
||||
|
||||
error: aborting due to 76 previous errors
|
||||
error: unnecessary use of `to_string`
|
||||
--> $DIR/unnecessary_to_owned.rs:260:24
|
||||
|
|
||||
LL | Box::new(build(y.to_string()))
|
||||
| ^^^^^^^^^^^^^ help: use: `y`
|
||||
|
||||
error: aborting due to 77 previous errors
|
||||
|
||||
|
|
Loading…
Reference in a new issue