mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-26 06:30:32 +00:00
Merge commit '43a1777b89cf6791f9e20878b4e5e3ae907867a5' into clippyup
This commit is contained in:
parent
51158cc71f
commit
d13d8987b0
34 changed files with 1573 additions and 256 deletions
|
@ -1422,7 +1422,9 @@ Released 2018-09-13
|
|||
[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
|
||||
[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
|
||||
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
|
||||
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
|
||||
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
|
||||
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
|
||||
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
|
||||
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
|
||||
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
|
||||
|
|
|
@ -31,7 +31,6 @@ path = "src/driver.rs"
|
|||
# begin automatic update
|
||||
clippy_lints = { version = "0.0.212", path = "clippy_lints" }
|
||||
# end automatic update
|
||||
regex = "1"
|
||||
semver = "0.9"
|
||||
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
|
||||
tempfile = { version = "3.1.0", optional = true }
|
||||
|
|
|
@ -247,6 +247,8 @@ mod literal_representation;
|
|||
mod loops;
|
||||
mod macro_use;
|
||||
mod main_recursion;
|
||||
mod manual_async_fn;
|
||||
mod manual_non_exhaustive;
|
||||
mod map_clone;
|
||||
mod map_unit_fn;
|
||||
mod match_on_vec_items;
|
||||
|
@ -628,6 +630,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&loops::WHILE_LET_ON_ITERATOR,
|
||||
¯o_use::MACRO_USE_IMPORTS,
|
||||
&main_recursion::MAIN_RECURSION,
|
||||
&manual_async_fn::MANUAL_ASYNC_FN,
|
||||
&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
|
||||
&map_clone::MAP_CLONE,
|
||||
&map_unit_fn::OPTION_MAP_UNIT_FN,
|
||||
&map_unit_fn::RESULT_MAP_UNIT_FN,
|
||||
|
@ -1054,7 +1058,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
let max_struct_bools = conf.max_struct_bools;
|
||||
store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools));
|
||||
store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap);
|
||||
store.register_late_pass(|| box wildcard_imports::WildcardImports);
|
||||
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
|
||||
store.register_late_pass(move || box wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports));
|
||||
store.register_early_pass(|| box macro_use::MacroUseImports);
|
||||
store.register_late_pass(|| box verbose_file_reads::VerboseFileReads);
|
||||
store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
|
||||
|
@ -1064,6 +1069,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
|
||||
store.register_late_pass(|| box if_let_mutex::IfLetMutex);
|
||||
store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
|
||||
store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
|
||||
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
|
||||
|
@ -1138,6 +1145,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP),
|
||||
LintId::of(&loops::EXPLICIT_ITER_LOOP),
|
||||
LintId::of(¯o_use::MACRO_USE_IMPORTS),
|
||||
LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
|
||||
LintId::of(&matches::MATCH_BOOL),
|
||||
LintId::of(&matches::SINGLE_MATCH_ELSE),
|
||||
LintId::of(&methods::FILTER_MAP),
|
||||
|
@ -1280,10 +1288,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&loops::WHILE_LET_LOOP),
|
||||
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
|
||||
LintId::of(&main_recursion::MAIN_RECURSION),
|
||||
LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
|
||||
LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(&map_clone::MAP_CLONE),
|
||||
LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||
LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
|
||||
LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
|
||||
LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
|
||||
LintId::of(&matches::MATCH_AS_REF),
|
||||
LintId::of(&matches::MATCH_OVERLAPPING_ARM),
|
||||
|
@ -1474,6 +1483,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
|
||||
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
|
||||
LintId::of(&main_recursion::MAIN_RECURSION),
|
||||
LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
|
||||
LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(&map_clone::MAP_CLONE),
|
||||
LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
|
||||
LintId::of(&matches::MATCH_OVERLAPPING_ARM),
|
||||
|
@ -1647,7 +1658,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&loops::NEVER_LOOP),
|
||||
LintId::of(&loops::REVERSE_RANGE_LOOP),
|
||||
LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
|
||||
LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
|
||||
LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
|
||||
LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT),
|
||||
LintId::of(&methods::CLONE_DOUBLE_REF),
|
||||
|
|
|
@ -10,7 +10,6 @@ use crate::utils::{
|
|||
};
|
||||
use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sext, sugg};
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -772,40 +771,48 @@ fn check_for_loop<'a, 'tcx>(
|
|||
|
||||
fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::Path(ref qpath) = expr.kind;
|
||||
if let QPath::Resolved(None, ref path) = *qpath;
|
||||
if let ExprKind::Path(qpath) = &expr.kind;
|
||||
if let QPath::Resolved(None, path) = qpath;
|
||||
if path.segments.len() == 1;
|
||||
if let Res::Local(local_id) = qpath_res(cx, qpath, expr.hir_id);
|
||||
// our variable!
|
||||
if local_id == var;
|
||||
then {
|
||||
return true;
|
||||
// our variable!
|
||||
local_id == var
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
#[derive(Clone, Copy)]
|
||||
enum OffsetSign {
|
||||
Positive,
|
||||
Negative,
|
||||
}
|
||||
|
||||
struct Offset {
|
||||
value: String,
|
||||
negate: bool,
|
||||
sign: OffsetSign,
|
||||
}
|
||||
|
||||
impl Offset {
|
||||
fn negative(s: String) -> Self {
|
||||
Self { value: s, negate: true }
|
||||
fn negative(value: String) -> Self {
|
||||
Self {
|
||||
value,
|
||||
sign: OffsetSign::Negative,
|
||||
}
|
||||
}
|
||||
|
||||
fn positive(s: String) -> Self {
|
||||
fn positive(value: String) -> Self {
|
||||
Self {
|
||||
value: s,
|
||||
negate: false,
|
||||
value,
|
||||
sign: OffsetSign::Positive,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FixedOffsetVar {
|
||||
var_name: String,
|
||||
struct FixedOffsetVar<'hir> {
|
||||
var: &'hir Expr<'hir>,
|
||||
offset: Offset,
|
||||
}
|
||||
|
||||
|
@ -819,10 +826,20 @@ fn is_slice_like<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'_>) -> bool {
|
|||
is_slice || is_type_diagnostic_item(cx, ty, sym!(vec_type)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
|
||||
}
|
||||
|
||||
fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> Option<FixedOffsetVar> {
|
||||
fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method, _, args) = expr.kind;
|
||||
if method.ident.name == sym!(clone);
|
||||
if args.len() == 1;
|
||||
if let Some(arg) = args.get(0);
|
||||
then { arg } else { expr }
|
||||
}
|
||||
}
|
||||
|
||||
fn get_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, idx: &Expr<'_>, var: HirId) -> Option<Offset> {
|
||||
fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId) -> Option<String> {
|
||||
match e.kind {
|
||||
ExprKind::Lit(ref l) => match l.node {
|
||||
match &e.kind {
|
||||
ExprKind::Lit(l) => match l.node {
|
||||
ast::LitKind::Int(x, _ty) => Some(x.to_string()),
|
||||
_ => None,
|
||||
},
|
||||
|
@ -831,115 +848,139 @@ fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, v
|
|||
}
|
||||
}
|
||||
|
||||
if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind {
|
||||
let ty = cx.tables.expr_ty(seqexpr);
|
||||
if !is_slice_like(cx, ty) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let offset = match idx.kind {
|
||||
ExprKind::Binary(op, ref lhs, ref rhs) => match op.node {
|
||||
BinOpKind::Add => {
|
||||
let offset_opt = if same_var(cx, lhs, var) {
|
||||
extract_offset(cx, rhs, var)
|
||||
} else if same_var(cx, rhs, var) {
|
||||
extract_offset(cx, lhs, var)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
offset_opt.map(Offset::positive)
|
||||
},
|
||||
BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative),
|
||||
_ => None,
|
||||
},
|
||||
ExprKind::Path(..) => {
|
||||
if same_var(cx, idx, var) {
|
||||
Some(Offset::positive("0".into()))
|
||||
match idx.kind {
|
||||
ExprKind::Binary(op, lhs, rhs) => match op.node {
|
||||
BinOpKind::Add => {
|
||||
let offset_opt = if same_var(cx, lhs, var) {
|
||||
extract_offset(cx, rhs, var)
|
||||
} else if same_var(cx, rhs, var) {
|
||||
extract_offset(cx, lhs, var)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
offset_opt.map(Offset::positive)
|
||||
},
|
||||
BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
offset.map(|o| FixedOffsetVar {
|
||||
var_name: snippet_opt(cx, seqexpr.span).unwrap_or_else(|| "???".into()),
|
||||
offset: o,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
},
|
||||
ExprKind::Path(..) if same_var(cx, idx, var) => Some(Offset::positive("0".into())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_cloned_fixed_offset_var<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
expr: &Expr<'_>,
|
||||
var: HirId,
|
||||
) -> Option<FixedOffsetVar> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(ref method, _, ref args) = expr.kind;
|
||||
if method.ident.name == sym!(clone);
|
||||
if args.len() == 1;
|
||||
if let Some(arg) = args.get(0);
|
||||
then {
|
||||
return get_fixed_offset_var(cx, arg, var);
|
||||
}
|
||||
}
|
||||
|
||||
get_fixed_offset_var(cx, expr, var)
|
||||
}
|
||||
|
||||
fn get_indexed_assignments<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
body: &Expr<'_>,
|
||||
var: HirId,
|
||||
) -> Vec<(FixedOffsetVar, FixedOffsetVar)> {
|
||||
fn get_assignment<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
e: &Expr<'_>,
|
||||
var: HirId,
|
||||
) -> Option<(FixedOffsetVar, FixedOffsetVar)> {
|
||||
if let ExprKind::Assign(ref lhs, ref rhs, _) = e.kind {
|
||||
match (
|
||||
get_fixed_offset_var(cx, lhs, var),
|
||||
fetch_cloned_fixed_offset_var(cx, rhs, var),
|
||||
) {
|
||||
(Some(offset_left), Some(offset_right)) => {
|
||||
// Source and destination must be different
|
||||
if offset_left.var_name == offset_right.var_name {
|
||||
None
|
||||
} else {
|
||||
Some((offset_left, offset_right))
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
fn get_assignments<'tcx>(body: &'tcx Expr<'tcx>) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> {
|
||||
fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
|
||||
if let ExprKind::Assign(lhs, rhs, _) = e.kind {
|
||||
Some((lhs, rhs))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
if let ExprKind::Block(ref b, _) = body.kind {
|
||||
let Block {
|
||||
ref stmts, ref expr, ..
|
||||
} = **b;
|
||||
// This is one of few ways to return different iterators
|
||||
// derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434
|
||||
let mut iter_a = None;
|
||||
let mut iter_b = None;
|
||||
|
||||
stmts
|
||||
if let ExprKind::Block(b, _) = body.kind {
|
||||
let Block { stmts, expr, .. } = *b;
|
||||
|
||||
iter_a = stmts
|
||||
.iter()
|
||||
.map(|stmt| match stmt.kind {
|
||||
.filter_map(|stmt| match stmt.kind {
|
||||
StmtKind::Local(..) | StmtKind::Item(..) => None,
|
||||
StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => Some(get_assignment(cx, e, var)),
|
||||
StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e),
|
||||
})
|
||||
.chain(expr.as_ref().into_iter().map(|e| Some(get_assignment(cx, &*e, var))))
|
||||
.filter_map(|op| op)
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap_or_default()
|
||||
.chain(expr.into_iter())
|
||||
.map(get_assignment)
|
||||
.into()
|
||||
} else {
|
||||
get_assignment(cx, body, var).into_iter().collect()
|
||||
iter_b = Some(get_assignment(body))
|
||||
}
|
||||
|
||||
iter_a.into_iter().flatten().chain(iter_b.into_iter())
|
||||
}
|
||||
|
||||
fn build_manual_memcpy_suggestion<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
start: &Expr<'_>,
|
||||
end: &Expr<'_>,
|
||||
limits: ast::RangeLimits,
|
||||
dst_var: FixedOffsetVar<'_>,
|
||||
src_var: FixedOffsetVar<'_>,
|
||||
) -> String {
|
||||
fn print_sum(arg1: &str, arg2: &Offset) -> String {
|
||||
match (arg1, &arg2.value[..], arg2.sign) {
|
||||
("0", "0", _) => "0".into(),
|
||||
("0", x, OffsetSign::Positive) | (x, "0", _) => x.into(),
|
||||
("0", x, OffsetSign::Negative) => format!("-{}", x),
|
||||
(x, y, OffsetSign::Positive) => format!("({} + {})", x, y),
|
||||
(x, y, OffsetSign::Negative) => {
|
||||
if x == y {
|
||||
"0".into()
|
||||
} else {
|
||||
format!("({} - {})", x, y)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn print_offset(start_str: &str, inline_offset: &Offset) -> String {
|
||||
let offset = print_sum(start_str, inline_offset);
|
||||
if offset.as_str() == "0" {
|
||||
"".into()
|
||||
} else {
|
||||
offset
|
||||
}
|
||||
}
|
||||
|
||||
let print_limit = |end: &Expr<'_>, offset: Offset, var: &Expr<'_>| {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method, _, len_args) = end.kind;
|
||||
if method.ident.name == sym!(len);
|
||||
if len_args.len() == 1;
|
||||
if let Some(arg) = len_args.get(0);
|
||||
if var_def_id(cx, arg) == var_def_id(cx, var);
|
||||
then {
|
||||
match offset.sign {
|
||||
OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value),
|
||||
OffsetSign::Positive => "".into(),
|
||||
}
|
||||
} else {
|
||||
let end_str = match limits {
|
||||
ast::RangeLimits::Closed => {
|
||||
let end = sugg::Sugg::hir(cx, end, "<count>");
|
||||
format!("{}", end + sugg::ONE)
|
||||
},
|
||||
ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")),
|
||||
};
|
||||
|
||||
print_sum(&end_str, &offset)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let start_str = snippet(cx, start.span, "").to_string();
|
||||
let dst_offset = print_offset(&start_str, &dst_var.offset);
|
||||
let dst_limit = print_limit(end, dst_var.offset, dst_var.var);
|
||||
let src_offset = print_offset(&start_str, &src_var.offset);
|
||||
let src_limit = print_limit(end, src_var.offset, src_var.var);
|
||||
|
||||
let dst_var_name = snippet_opt(cx, dst_var.var.span).unwrap_or_else(|| "???".into());
|
||||
let src_var_name = snippet_opt(cx, src_var.var.span).unwrap_or_else(|| "???".into());
|
||||
|
||||
let dst = if dst_offset == "" && dst_limit == "" {
|
||||
dst_var_name
|
||||
} else {
|
||||
format!("{}[{}..{}]", dst_var_name, dst_offset, dst_limit)
|
||||
};
|
||||
|
||||
format!(
|
||||
"{}.clone_from_slice(&{}[{}..{}])",
|
||||
dst, src_var_name, src_offset, src_limit
|
||||
)
|
||||
}
|
||||
/// Checks for for loops that sequentially copy items from one slice-like
|
||||
/// object to another.
|
||||
fn detect_manual_memcpy<'a, 'tcx>(
|
||||
|
@ -951,93 +992,43 @@ fn detect_manual_memcpy<'a, 'tcx>(
|
|||
) {
|
||||
if let Some(higher::Range {
|
||||
start: Some(start),
|
||||
ref end,
|
||||
end: Some(end),
|
||||
limits,
|
||||
}) = higher::range(cx, arg)
|
||||
{
|
||||
// the var must be a single name
|
||||
if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
|
||||
let print_sum = |arg1: &Offset, arg2: &Offset| -> String {
|
||||
match (&arg1.value[..], arg1.negate, &arg2.value[..], arg2.negate) {
|
||||
("0", _, "0", _) => "".into(),
|
||||
("0", _, x, false) | (x, false, "0", false) => x.into(),
|
||||
("0", _, x, true) | (x, false, "0", true) => format!("-{}", x),
|
||||
(x, false, y, false) => format!("({} + {})", x, y),
|
||||
(x, false, y, true) => {
|
||||
if x == y {
|
||||
"0".into()
|
||||
} else {
|
||||
format!("({} - {})", x, y)
|
||||
}
|
||||
},
|
||||
(x, true, y, false) => {
|
||||
if x == y {
|
||||
"0".into()
|
||||
} else {
|
||||
format!("({} - {})", y, x)
|
||||
}
|
||||
},
|
||||
(x, true, y, true) => format!("-({} + {})", x, y),
|
||||
}
|
||||
};
|
||||
|
||||
let print_limit = |end: &Option<&Expr<'_>>, offset: Offset, var_name: &str| {
|
||||
if let Some(end) = *end {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(ref method, _, ref len_args) = end.kind;
|
||||
if method.ident.name == sym!(len);
|
||||
if len_args.len() == 1;
|
||||
if let Some(arg) = len_args.get(0);
|
||||
if snippet(cx, arg.span, "??") == var_name;
|
||||
then {
|
||||
return if offset.negate {
|
||||
format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let end_str = match limits {
|
||||
ast::RangeLimits::Closed => {
|
||||
let end = sugg::Sugg::hir(cx, end, "<count>");
|
||||
format!("{}", end + sugg::ONE)
|
||||
},
|
||||
ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")),
|
||||
};
|
||||
|
||||
print_sum(&Offset::positive(end_str), &offset)
|
||||
} else {
|
||||
"..".into()
|
||||
}
|
||||
};
|
||||
|
||||
// The only statements in the for loops can be indexed assignments from
|
||||
// indexed retrievals.
|
||||
let manual_copies = get_indexed_assignments(cx, body, canonical_id);
|
||||
let big_sugg = get_assignments(body)
|
||||
.map(|o| {
|
||||
o.and_then(|(lhs, rhs)| {
|
||||
let rhs = fetch_cloned_expr(rhs);
|
||||
if_chain! {
|
||||
if let ExprKind::Index(seqexpr_left, idx_left) = lhs.kind;
|
||||
if let ExprKind::Index(seqexpr_right, idx_right) = rhs.kind;
|
||||
if is_slice_like(cx, cx.tables.expr_ty(seqexpr_left))
|
||||
&& is_slice_like(cx, cx.tables.expr_ty(seqexpr_right));
|
||||
if let Some(offset_left) = get_offset(cx, &idx_left, canonical_id);
|
||||
if let Some(offset_right) = get_offset(cx, &idx_right, canonical_id);
|
||||
|
||||
let big_sugg = manual_copies
|
||||
.into_iter()
|
||||
.map(|(dst_var, src_var)| {
|
||||
let start_str = Offset::positive(snippet(cx, start.span, "").to_string());
|
||||
let dst_offset = print_sum(&start_str, &dst_var.offset);
|
||||
let dst_limit = print_limit(end, dst_var.offset, &dst_var.var_name);
|
||||
let src_offset = print_sum(&start_str, &src_var.offset);
|
||||
let src_limit = print_limit(end, src_var.offset, &src_var.var_name);
|
||||
let dst = if dst_offset == "" && dst_limit == "" {
|
||||
dst_var.var_name
|
||||
} else {
|
||||
format!("{}[{}..{}]", dst_var.var_name, dst_offset, dst_limit)
|
||||
};
|
||||
|
||||
format!(
|
||||
"{}.clone_from_slice(&{}[{}..{}])",
|
||||
dst, src_var.var_name, src_offset, src_limit
|
||||
)
|
||||
// Source and destination must be different
|
||||
if var_def_id(cx, seqexpr_left) != var_def_id(cx, seqexpr_right);
|
||||
then {
|
||||
Some((FixedOffsetVar { var: seqexpr_left, offset: offset_left },
|
||||
FixedOffsetVar { var: seqexpr_right, offset: offset_right }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.join("\n ");
|
||||
.map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, dst, src)))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| v.join("\n "));
|
||||
|
||||
if !big_sugg.is_empty() {
|
||||
if let Some(big_sugg) = big_sugg {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_MEMCPY,
|
||||
|
|
159
clippy_lints/src/manual_async_fn.rs
Normal file
159
clippy_lints/src/manual_async_fn.rs
Normal file
|
@ -0,0 +1,159 @@
|
|||
use crate::utils::paths::FUTURE_FROM_GENERATOR;
|
||||
use crate::utils::{match_function_call, snippet_block, snippet_opt, span_lint_and_then};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync,
|
||||
ItemKind, TraitRef, Ty, TyKind, TypeBindingKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** It checks for manual implementations of `async` functions.
|
||||
///
|
||||
/// **Why is this bad?** It's more idiomatic to use the dedicated syntax.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::future::Future;
|
||||
///
|
||||
/// fn foo() -> impl Future<Output = i32> { async { 42 } }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::future::Future;
|
||||
///
|
||||
/// async fn foo() -> i32 { 42 }
|
||||
/// ```
|
||||
pub MANUAL_ASYNC_FN,
|
||||
style,
|
||||
"manual implementations of `async` functions can be simplified using the dedicated syntax"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualAsyncFn => [MANUAL_ASYNC_FN]);
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ManualAsyncFn {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
kind: FnKind<'tcx>,
|
||||
decl: &'tcx FnDecl<'_>,
|
||||
body: &'tcx Body<'_>,
|
||||
span: Span,
|
||||
_: HirId,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(header) = kind.header();
|
||||
if let IsAsync::NotAsync = header.asyncness;
|
||||
// Check that this function returns `impl Future`
|
||||
if let FnRetTy::Return(ret_ty) = decl.output;
|
||||
if let Some(trait_ref) = future_trait_ref(cx, ret_ty);
|
||||
if let Some(output) = future_output_ty(trait_ref);
|
||||
// Check that the body of the function consists of one async block
|
||||
if let ExprKind::Block(block, _) = body.value.kind;
|
||||
if block.stmts.is_empty();
|
||||
if let Some(closure_body) = desugared_async_block(cx, block);
|
||||
then {
|
||||
let header_span = span.with_hi(ret_ty.span.hi());
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_ASYNC_FN,
|
||||
header_span,
|
||||
"this function can be simplified using the `async fn` syntax",
|
||||
|diag| {
|
||||
if_chain! {
|
||||
if let Some(header_snip) = snippet_opt(cx, header_span);
|
||||
if let Some(ret_pos) = header_snip.rfind("->");
|
||||
if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
|
||||
then {
|
||||
let help = format!("make the function `async` and {}", ret_sugg);
|
||||
diag.span_suggestion(
|
||||
header_span,
|
||||
&help,
|
||||
format!("async {}{}", &header_snip[..ret_pos], ret_snip),
|
||||
Applicability::MachineApplicable
|
||||
);
|
||||
|
||||
let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span));
|
||||
diag.span_suggestion(
|
||||
block.span,
|
||||
"move the body of the async block to the enclosing function",
|
||||
body_snip.to_string(),
|
||||
Applicability::MachineApplicable
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn future_trait_ref<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> {
|
||||
if_chain! {
|
||||
if let TyKind::Def(item_id, _) = ty.kind;
|
||||
let item = cx.tcx.hir().item(item_id.id);
|
||||
if let ItemKind::OpaqueTy(opaque) = &item.kind;
|
||||
if opaque.bounds.len() == 1;
|
||||
if let GenericBound::Trait(poly, _) = &opaque.bounds[0];
|
||||
if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
|
||||
then {
|
||||
return Some(&poly.trait_ref);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let Some(segment) = trait_ref.path.segments.last();
|
||||
if let Some(args) = segment.args;
|
||||
if args.bindings.len() == 1;
|
||||
let binding = &args.bindings[0];
|
||||
if binding.ident.as_str() == "Output";
|
||||
if let TypeBindingKind::Equality{ty: output} = binding.kind;
|
||||
then {
|
||||
return Some(output)
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn desugared_async_block<'tcx>(cx: &LateContext<'_, 'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
|
||||
if_chain! {
|
||||
if let Some(block_expr) = block.expr;
|
||||
if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR);
|
||||
if args.len() == 1;
|
||||
if let Expr{kind: ExprKind::Closure(_, _, body_id, ..), ..} = args[0];
|
||||
let closure_body = cx.tcx.hir().body(body_id);
|
||||
if let Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) = closure_body.generator_kind;
|
||||
then {
|
||||
return Some(closure_body);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn suggested_ret(cx: &LateContext<'_, '_>, output: &Ty<'_>) -> Option<(&'static str, String)> {
|
||||
match output.kind {
|
||||
TyKind::Tup(tys) if tys.is_empty() => {
|
||||
let sugg = "remove the return type";
|
||||
Some((sugg, "".into()))
|
||||
},
|
||||
_ => {
|
||||
let sugg = "return the output of the future directly";
|
||||
snippet_opt(cx, output.span).map(|snip| (sugg, format!("-> {}", snip)))
|
||||
},
|
||||
}
|
||||
}
|
173
clippy_lints/src/manual_non_exhaustive.rs
Normal file
173
clippy_lints/src/manual_non_exhaustive.rs
Normal file
|
@ -0,0 +1,173 @@
|
|||
use crate::utils::{snippet_opt, span_lint_and_then};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind};
|
||||
use rustc_attr as attr;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
|
||||
///
|
||||
/// **Why is this bad?** Using the #[non_exhaustive] attribute expresses better the intent
|
||||
/// and allows possible optimizations when applied to enums.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// struct S {
|
||||
/// pub a: i32,
|
||||
/// pub b: i32,
|
||||
/// _c: (),
|
||||
/// }
|
||||
///
|
||||
/// enum E {
|
||||
/// A,
|
||||
/// B,
|
||||
/// #[doc(hidden)]
|
||||
/// _C,
|
||||
/// }
|
||||
///
|
||||
/// struct T(pub i32, pub i32, ());
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// #[non_exhaustive]
|
||||
/// struct S {
|
||||
/// pub a: i32,
|
||||
/// pub b: i32,
|
||||
/// }
|
||||
///
|
||||
/// #[non_exhaustive]
|
||||
/// enum E {
|
||||
/// A,
|
||||
/// B,
|
||||
/// }
|
||||
///
|
||||
/// #[non_exhaustive]
|
||||
/// struct T(pub i32, pub i32);
|
||||
/// ```
|
||||
pub MANUAL_NON_EXHAUSTIVE,
|
||||
style,
|
||||
"manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
|
||||
|
||||
impl EarlyLintPass for ManualNonExhaustive {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
match &item.kind {
|
||||
ItemKind::Enum(def, _) => {
|
||||
check_manual_non_exhaustive_enum(cx, item, &def.variants);
|
||||
},
|
||||
ItemKind::Struct(variant_data, _) => {
|
||||
if let VariantData::Unit(..) = variant_data {
|
||||
return;
|
||||
}
|
||||
|
||||
check_manual_non_exhaustive_struct(cx, item, variant_data);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) {
|
||||
fn is_non_exhaustive_marker(variant: &Variant) -> bool {
|
||||
matches!(variant.data, VariantData::Unit(_))
|
||||
&& variant.ident.as_str().starts_with('_')
|
||||
&& variant.attrs.iter().any(|a| is_doc_hidden(a))
|
||||
}
|
||||
|
||||
fn is_doc_hidden(attr: &Attribute) -> bool {
|
||||
attr.check_name(sym!(doc))
|
||||
&& match attr.meta_item_list() {
|
||||
Some(l) => attr::list_contains_name(&l, sym!(hidden)),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v));
|
||||
if let Some(marker) = markers.next();
|
||||
if markers.count() == 0 && variants.len() > 1;
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_NON_EXHAUSTIVE,
|
||||
item.span,
|
||||
"this seems like a manual implementation of the non-exhaustive pattern",
|
||||
|diag| {
|
||||
if_chain! {
|
||||
if !attr::contains_name(&item.attrs, sym!(non_exhaustive));
|
||||
let header_span = cx.sess.source_map().span_until_char(item.span, '{');
|
||||
if let Some(snippet) = snippet_opt(cx, header_span);
|
||||
then {
|
||||
diag.span_suggestion(
|
||||
header_span,
|
||||
"add the attribute",
|
||||
format!("#[non_exhaustive] {}", snippet),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
}
|
||||
}
|
||||
diag.span_help(marker.span, "remove this variant");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) {
|
||||
fn is_private(field: &StructField) -> bool {
|
||||
matches!(field.vis.node, VisibilityKind::Inherited)
|
||||
}
|
||||
|
||||
fn is_non_exhaustive_marker(field: &StructField) -> bool {
|
||||
is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_'))
|
||||
}
|
||||
|
||||
fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span {
|
||||
let delimiter = match data {
|
||||
VariantData::Struct(..) => '{',
|
||||
VariantData::Tuple(..) => '(',
|
||||
VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"),
|
||||
};
|
||||
|
||||
cx.sess.source_map().span_until_char(item.span, delimiter)
|
||||
}
|
||||
|
||||
let fields = data.fields();
|
||||
let private_fields = fields.iter().filter(|f| is_private(f)).count();
|
||||
let public_fields = fields.iter().filter(|f| f.vis.node.is_pub()).count();
|
||||
|
||||
if_chain! {
|
||||
if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1;
|
||||
if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f));
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_NON_EXHAUSTIVE,
|
||||
item.span,
|
||||
"this seems like a manual implementation of the non-exhaustive pattern",
|
||||
|diag| {
|
||||
if_chain! {
|
||||
if !attr::contains_name(&item.attrs, sym!(non_exhaustive));
|
||||
let header_span = find_header_span(cx, item, data);
|
||||
if let Some(snippet) = snippet_opt(cx, header_span);
|
||||
then {
|
||||
diag.span_suggestion(
|
||||
header_span,
|
||||
"add the attribute",
|
||||
format!("#[non_exhaustive] {}", snippet),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
}
|
||||
}
|
||||
diag.span_help(marker.span, "remove this field");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty};
|
||||
use crate::utils::{self, is_type_diagnostic_item, match_type, snippet, span_lint_and_sugg, walk_ptrs_ty};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource};
|
||||
|
@ -38,7 +38,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
pub MATCH_ON_VEC_ITEMS,
|
||||
correctness,
|
||||
pedantic,
|
||||
"matching on vector elements can panic"
|
||||
}
|
||||
|
||||
|
@ -75,10 +75,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchOnVecItems {
|
|||
|
||||
fn is_vec_indexing<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if_chain! {
|
||||
if let ExprKind::Index(ref array, _) = expr.kind;
|
||||
let ty = cx.tables.expr_ty(array);
|
||||
let ty = walk_ptrs_ty(ty);
|
||||
if is_type_diagnostic_item(cx, ty, sym!(vec_type));
|
||||
if let ExprKind::Index(ref array, ref index) = expr.kind;
|
||||
if is_vector(cx, array);
|
||||
if !is_full_range(cx, index);
|
||||
|
||||
then {
|
||||
return Some(expr);
|
||||
|
@ -87,3 +86,15 @@ fn is_vec_indexing<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>)
|
|||
|
||||
None
|
||||
}
|
||||
|
||||
fn is_vector(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
|
||||
let ty = cx.tables.expr_ty(expr);
|
||||
let ty = walk_ptrs_ty(ty);
|
||||
is_type_diagnostic_item(cx, ty, sym!(vec_type))
|
||||
}
|
||||
|
||||
fn is_full_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
|
||||
let ty = cx.tables.expr_ty(expr);
|
||||
let ty = walk_ptrs_ty(ty);
|
||||
match_type(cx, ty, &utils::paths::RANGE_FULL)
|
||||
}
|
||||
|
|
|
@ -24,8 +24,25 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// let { a: _, b: ref b, c: _ } = ..
|
||||
/// ```rust
|
||||
/// # struct Foo {
|
||||
/// # a: i32,
|
||||
/// # b: i32,
|
||||
/// # c: i32,
|
||||
/// # }
|
||||
/// let f = Foo { a: 0, b: 0, c: 0 };
|
||||
///
|
||||
/// // Bad
|
||||
/// match f {
|
||||
/// Foo { a: _, b: 0, .. } => {},
|
||||
/// Foo { a: _, b: _, c: _ } => {},
|
||||
/// }
|
||||
///
|
||||
/// // Good
|
||||
/// match f {
|
||||
/// Foo { b: 0, .. } => {},
|
||||
/// Foo { .. } => {},
|
||||
/// }
|
||||
/// ```
|
||||
pub UNNEEDED_FIELD_PATTERN,
|
||||
restriction,
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use crate::utils::{higher::if_block, is_type_diagnostic_item, span_lint_and_then, usage::is_potentially_mutated};
|
||||
use crate::utils::{
|
||||
differing_macro_contexts, higher::if_block, is_type_diagnostic_item, span_lint_and_then,
|
||||
usage::is_potentially_mutated,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp};
|
||||
|
@ -73,6 +76,8 @@ struct UnwrapInfo<'tcx> {
|
|||
ident: &'tcx Path<'tcx>,
|
||||
/// The check, like `x.is_ok()`
|
||||
check: &'tcx Expr<'tcx>,
|
||||
/// The branch where the check takes place, like `if x.is_ok() { .. }`
|
||||
branch: &'tcx Expr<'tcx>,
|
||||
/// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
|
||||
safe_to_unwrap: bool,
|
||||
}
|
||||
|
@ -82,19 +87,20 @@ struct UnwrapInfo<'tcx> {
|
|||
fn collect_unwrap_info<'a, 'tcx>(
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
branch: &'tcx Expr<'_>,
|
||||
invert: bool,
|
||||
) -> Vec<UnwrapInfo<'tcx>> {
|
||||
if let ExprKind::Binary(op, left, right) = &expr.kind {
|
||||
match (invert, op.node) {
|
||||
(false, BinOpKind::And) | (false, BinOpKind::BitAnd) | (true, BinOpKind::Or) | (true, BinOpKind::BitOr) => {
|
||||
let mut unwrap_info = collect_unwrap_info(cx, left, invert);
|
||||
unwrap_info.append(&mut collect_unwrap_info(cx, right, invert));
|
||||
let mut unwrap_info = collect_unwrap_info(cx, left, branch, invert);
|
||||
unwrap_info.append(&mut collect_unwrap_info(cx, right, branch, invert));
|
||||
return unwrap_info;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
} else if let ExprKind::Unary(UnOp::UnNot, expr) = &expr.kind {
|
||||
return collect_unwrap_info(cx, expr, !invert);
|
||||
return collect_unwrap_info(cx, expr, branch, !invert);
|
||||
} else {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, _, args) = &expr.kind;
|
||||
|
@ -111,7 +117,7 @@ fn collect_unwrap_info<'a, 'tcx>(
|
|||
_ => unreachable!(),
|
||||
};
|
||||
let safe_to_unwrap = unwrappable != invert;
|
||||
return vec![UnwrapInfo { ident: path, check: expr, safe_to_unwrap }];
|
||||
return vec![UnwrapInfo { ident: path, check: expr, branch, safe_to_unwrap }];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +127,7 @@ fn collect_unwrap_info<'a, 'tcx>(
|
|||
impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
|
||||
fn visit_branch(&mut self, cond: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, else_branch: bool) {
|
||||
let prev_len = self.unwrappables.len();
|
||||
for unwrap_info in collect_unwrap_info(self.cx, cond, else_branch) {
|
||||
for unwrap_info in collect_unwrap_info(self.cx, cond, branch, else_branch) {
|
||||
if is_potentially_mutated(unwrap_info.ident, cond, self.cx)
|
||||
|| is_potentially_mutated(unwrap_info.ident, branch, self.cx)
|
||||
{
|
||||
|
@ -158,6 +164,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
|
|||
let call_to_unwrap = method_name.ident.name == sym!(unwrap);
|
||||
if let Some(unwrappable) = self.unwrappables.iter()
|
||||
.find(|u| u.ident.res == path.res);
|
||||
// Span contexts should not differ with the conditional branch
|
||||
if !differing_macro_contexts(unwrappable.branch.span, expr.span);
|
||||
if !differing_macro_contexts(unwrappable.branch.span, unwrappable.check.span);
|
||||
then {
|
||||
if call_to_unwrap == unwrappable.safe_to_unwrap {
|
||||
span_lint_and_then(
|
||||
|
|
|
@ -158,6 +158,8 @@ define_Conf! {
|
|||
(max_struct_bools, "max_struct_bools": u64, 3),
|
||||
/// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
|
||||
(max_fn_params_bools, "max_fn_params_bools": u64, 3),
|
||||
/// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
|
||||
(warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
|
||||
}
|
||||
|
||||
impl Default for Conf {
|
||||
|
|
|
@ -933,6 +933,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_, '_>, expr: &Exp
|
|||
}
|
||||
|
||||
/// Returns `true` if a pattern is refutable.
|
||||
// TODO: should be implemented using rustc/mir_build/hair machinery
|
||||
pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool {
|
||||
fn is_enum_variant(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, id: HirId) -> bool {
|
||||
matches!(
|
||||
|
@ -946,27 +947,34 @@ pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool {
|
|||
}
|
||||
|
||||
match pat.kind {
|
||||
PatKind::Binding(..) | PatKind::Wild => false,
|
||||
PatKind::Wild => false,
|
||||
PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
|
||||
PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat),
|
||||
PatKind::Lit(..) | PatKind::Range(..) => true,
|
||||
PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
|
||||
PatKind::Or(ref pats) | PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
|
||||
PatKind::Or(ref pats) => {
|
||||
// TODO: should be the honest check, that pats is exhaustive set
|
||||
are_refutable(cx, pats.iter().map(|pat| &**pat))
|
||||
},
|
||||
PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
|
||||
PatKind::Struct(ref qpath, ref fields, _) => {
|
||||
if is_enum_variant(cx, qpath, pat.hir_id) {
|
||||
true
|
||||
} else {
|
||||
are_refutable(cx, fields.iter().map(|field| &*field.pat))
|
||||
}
|
||||
is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
|
||||
},
|
||||
PatKind::TupleStruct(ref qpath, ref pats, _) => {
|
||||
if is_enum_variant(cx, qpath, pat.hir_id) {
|
||||
true
|
||||
} else {
|
||||
are_refutable(cx, pats.iter().map(|pat| &**pat))
|
||||
}
|
||||
is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat))
|
||||
},
|
||||
PatKind::Slice(ref head, ref middle, ref tail) => {
|
||||
are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat))
|
||||
match &cx.tables.node_type(pat.hir_id).kind {
|
||||
ty::Slice(..) => {
|
||||
// [..] is the only irrefutable slice pattern.
|
||||
!head.is_empty() || middle.is_none() || !tail.is_empty()
|
||||
},
|
||||
ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)),
|
||||
_ => {
|
||||
// unreachable!()
|
||||
true
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments
|
|||
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
|
||||
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
|
||||
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
|
||||
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
|
||||
pub const HASH: [&str; 2] = ["hash", "Hash"];
|
||||
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
|
||||
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
|
||||
|
@ -85,7 +86,7 @@ pub const RANGE: [&str; 3] = ["core", "ops", "Range"];
|
|||
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
|
||||
pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"];
|
||||
pub const RANGE_FROM_STD: [&str; 3] = ["std", "ops", "RangeFrom"];
|
||||
pub const RANGE_FULL: [&str; 3] = ["core", "ops", "RangeFull"];
|
||||
pub const RANGE_FULL: [&str; 4] = ["core", "ops", "range", "RangeFull"];
|
||||
pub const RANGE_FULL_STD: [&str; 3] = ["std", "ops", "RangeFull"];
|
||||
pub const RANGE_INCLUSIVE_NEW: [&str; 4] = ["core", "ops", "RangeInclusive", "new"];
|
||||
pub const RANGE_INCLUSIVE_STD_NEW: [&str; 4] = ["std", "ops", "RangeInclusive", "new"];
|
||||
|
|
|
@ -3,10 +3,10 @@ use if_chain::if_chain;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
def::{DefKind, Res},
|
||||
Item, ItemKind, UseKind,
|
||||
Item, ItemKind, PathSegment, UseKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::BytePos;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -43,9 +43,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// This can lead to confusing error messages at best and to unexpected behavior at worst.
|
||||
///
|
||||
/// Note that this will not warn about wildcard imports from modules named `prelude`; many
|
||||
/// crates (including the standard library) provide modules named "prelude" specifically
|
||||
/// designed for wildcard import.
|
||||
/// **Exceptions:**
|
||||
///
|
||||
/// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)
|
||||
/// provide modules named "prelude" specifically designed for wildcard import.
|
||||
///
|
||||
/// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name.
|
||||
///
|
||||
/// These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.
|
||||
///
|
||||
/// **Known problems:** If macros are imported through the wildcard, this macro is not included
|
||||
/// by the suggestion and has to be added by hand.
|
||||
|
@ -73,18 +78,34 @@ declare_clippy_lint! {
|
|||
"lint `use _::*` statements"
|
||||
}
|
||||
|
||||
declare_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
|
||||
#[derive(Default)]
|
||||
pub struct WildcardImports {
|
||||
warn_on_all: bool,
|
||||
test_modules_deep: u32,
|
||||
}
|
||||
|
||||
impl WildcardImports {
|
||||
pub fn new(warn_on_all: bool) -> Self {
|
||||
Self {
|
||||
warn_on_all,
|
||||
test_modules_deep: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
|
||||
|
||||
impl LateLintPass<'_, '_> for WildcardImports {
|
||||
fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) {
|
||||
if is_test_module_or_function(item) {
|
||||
self.test_modules_deep = self.test_modules_deep.saturating_add(1);
|
||||
}
|
||||
if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
|
||||
return;
|
||||
}
|
||||
if_chain! {
|
||||
if !in_macro(item.span);
|
||||
if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind;
|
||||
// don't lint prelude glob imports
|
||||
if !use_path.segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude");
|
||||
if self.warn_on_all || !self.check_exceptions(item, use_path.segments);
|
||||
let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner);
|
||||
if !used_imports.is_empty(); // Already handled by `unused_imports`
|
||||
then {
|
||||
|
@ -109,8 +130,7 @@ impl LateLintPass<'_, '_> for WildcardImports {
|
|||
span = use_path.span.with_hi(item.span.hi() - BytePos(1));
|
||||
}
|
||||
(
|
||||
span,
|
||||
false,
|
||||
span, false,
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -153,4 +173,36 @@ impl LateLintPass<'_, '_> for WildcardImports {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, _: &LateContext<'_, '_>, item: &Item<'_>) {
|
||||
if is_test_module_or_function(item) {
|
||||
self.test_modules_deep = self.test_modules_deep.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WildcardImports {
|
||||
fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
|
||||
in_macro(item.span)
|
||||
|| is_prelude_import(segments)
|
||||
|| (is_super_only_import(segments) && self.test_modules_deep > 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Allow "...prelude::*" imports.
|
||||
// Many crates have a prelude, and it is imported as a glob by design.
|
||||
fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
|
||||
segments
|
||||
.iter()
|
||||
.last()
|
||||
.map_or(false, |ps| ps.ident.as_str() == "prelude")
|
||||
}
|
||||
|
||||
// Allow "super::*" imports in tests.
|
||||
fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool {
|
||||
segments.len() == 1 && segments[0].ident.as_str() == "super"
|
||||
}
|
||||
|
||||
fn is_test_module_or_function(item: &Item<'_>) -> bool {
|
||||
matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test")
|
||||
}
|
||||
|
|
|
@ -1081,6 +1081,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
|||
deprecation: None,
|
||||
module: "main_recursion",
|
||||
},
|
||||
Lint {
|
||||
name: "manual_async_fn",
|
||||
group: "style",
|
||||
desc: "manual implementations of `async` functions can be simplified using the dedicated syntax",
|
||||
deprecation: None,
|
||||
module: "manual_async_fn",
|
||||
},
|
||||
Lint {
|
||||
name: "manual_memcpy",
|
||||
group: "perf",
|
||||
|
@ -1088,6 +1095,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
|||
deprecation: None,
|
||||
module: "loops",
|
||||
},
|
||||
Lint {
|
||||
name: "manual_non_exhaustive",
|
||||
group: "style",
|
||||
desc: "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]",
|
||||
deprecation: None,
|
||||
module: "manual_non_exhaustive",
|
||||
},
|
||||
Lint {
|
||||
name: "manual_saturating_arithmetic",
|
||||
group: "style",
|
||||
|
@ -1146,7 +1160,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
|||
},
|
||||
Lint {
|
||||
name: "match_on_vec_items",
|
||||
group: "correctness",
|
||||
group: "pedantic",
|
||||
desc: "matching on vector elements can panic",
|
||||
deprecation: None,
|
||||
module: "match_on_vec_items",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `third-party` at line 5 column 1
|
||||
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@ LL | 42
|
|||
|
|
||||
= note: expected type parameter `u32`
|
||||
found type `{integer}`
|
||||
= help: type parameters must be constrained to match other types
|
||||
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
@ -9,6 +9,30 @@ macro_rules! m {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! checks_in_param {
|
||||
($a:expr, $b:expr) => {
|
||||
if $a {
|
||||
$b;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! checks_unwrap {
|
||||
($a:expr, $b:expr) => {
|
||||
if $a.is_some() {
|
||||
$b;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! checks_some {
|
||||
($a:expr, $b:expr) => {
|
||||
if $a {
|
||||
$b.unwrap();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = Some(());
|
||||
if x.is_some() {
|
||||
|
@ -22,6 +46,9 @@ fn main() {
|
|||
x.unwrap(); // unnecessary
|
||||
}
|
||||
m!(x);
|
||||
checks_in_param!(x.is_some(), x.unwrap()); // ok
|
||||
checks_unwrap!(x, x.unwrap()); // ok
|
||||
checks_some!(x.is_some(), x); // ok
|
||||
let mut x: Result<(), ()> = Ok(());
|
||||
if x.is_ok() {
|
||||
x.unwrap(); // unnecessary
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
--> $DIR/simple_conditionals.rs:15:9
|
||||
--> $DIR/simple_conditionals.rs:39:9
|
||||
|
|
||||
LL | if x.is_some() {
|
||||
| ----------- the check is happening here
|
||||
|
@ -13,7 +13,7 @@ LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
|
|||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
--> $DIR/simple_conditionals.rs:17:9
|
||||
--> $DIR/simple_conditionals.rs:41:9
|
||||
|
|
||||
LL | if x.is_some() {
|
||||
| ----------- because of this check
|
||||
|
@ -28,7 +28,7 @@ LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
|
|||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
--> $DIR/simple_conditionals.rs:20:9
|
||||
--> $DIR/simple_conditionals.rs:44:9
|
||||
|
|
||||
LL | if x.is_none() {
|
||||
| ----------- because of this check
|
||||
|
@ -36,7 +36,7 @@ LL | x.unwrap(); // will panic
|
|||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
--> $DIR/simple_conditionals.rs:22:9
|
||||
--> $DIR/simple_conditionals.rs:46:9
|
||||
|
|
||||
LL | if x.is_none() {
|
||||
| ----------- the check is happening here
|
||||
|
@ -58,7 +58,7 @@ LL | m!(x);
|
|||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
--> $DIR/simple_conditionals.rs:27:9
|
||||
--> $DIR/simple_conditionals.rs:54:9
|
||||
|
|
||||
LL | if x.is_ok() {
|
||||
| --------- the check is happening here
|
||||
|
@ -66,7 +66,7 @@ LL | x.unwrap(); // unnecessary
|
|||
| ^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap_err()` will always panic.
|
||||
--> $DIR/simple_conditionals.rs:28:9
|
||||
--> $DIR/simple_conditionals.rs:55:9
|
||||
|
|
||||
LL | if x.is_ok() {
|
||||
| --------- because of this check
|
||||
|
@ -75,7 +75,7 @@ LL | x.unwrap_err(); // will panic
|
|||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
--> $DIR/simple_conditionals.rs:30:9
|
||||
--> $DIR/simple_conditionals.rs:57:9
|
||||
|
|
||||
LL | if x.is_ok() {
|
||||
| --------- because of this check
|
||||
|
@ -84,7 +84,7 @@ LL | x.unwrap(); // will panic
|
|||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
--> $DIR/simple_conditionals.rs:31:9
|
||||
--> $DIR/simple_conditionals.rs:58:9
|
||||
|
|
||||
LL | if x.is_ok() {
|
||||
| --------- the check is happening here
|
||||
|
@ -93,7 +93,7 @@ LL | x.unwrap_err(); // unnecessary
|
|||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap()` will always panic.
|
||||
--> $DIR/simple_conditionals.rs:34:9
|
||||
--> $DIR/simple_conditionals.rs:61:9
|
||||
|
|
||||
LL | if x.is_err() {
|
||||
| ---------- because of this check
|
||||
|
@ -101,7 +101,7 @@ LL | x.unwrap(); // will panic
|
|||
| ^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
--> $DIR/simple_conditionals.rs:35:9
|
||||
--> $DIR/simple_conditionals.rs:62:9
|
||||
|
|
||||
LL | if x.is_err() {
|
||||
| ---------- the check is happening here
|
||||
|
@ -110,7 +110,7 @@ LL | x.unwrap_err(); // unnecessary
|
|||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
|
||||
--> $DIR/simple_conditionals.rs:37:9
|
||||
--> $DIR/simple_conditionals.rs:64:9
|
||||
|
|
||||
LL | if x.is_err() {
|
||||
| ---------- the check is happening here
|
||||
|
@ -119,7 +119,7 @@ LL | x.unwrap(); // unnecessary
|
|||
| ^^^^^^^^^^
|
||||
|
||||
error: This call to `unwrap_err()` will always panic.
|
||||
--> $DIR/simple_conditionals.rs:38:9
|
||||
--> $DIR/simple_conditionals.rs:65:9
|
||||
|
|
||||
LL | if x.is_err() {
|
||||
| ---------- because of this check
|
||||
|
|
|
@ -41,6 +41,7 @@ impl Dummy {
|
|||
self.private_future().await;
|
||||
}
|
||||
|
||||
#[allow(clippy::manual_async_fn)]
|
||||
pub fn public_send(&self) -> impl std::future::Future<Output = bool> {
|
||||
async { false }
|
||||
}
|
||||
|
|
|
@ -96,13 +96,13 @@ LL | }
|
|||
= note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
|
||||
|
||||
error: future cannot be sent between threads safely
|
||||
--> $DIR/future_not_send.rs:49:37
|
||||
--> $DIR/future_not_send.rs:50:37
|
||||
|
|
||||
LL | async fn generic_future<T>(t: T) -> T
|
||||
| ^ future returned by `generic_future` is not `Send`
|
||||
|
|
||||
note: future is not `Send` as this value is used across an await
|
||||
--> $DIR/future_not_send.rs:54:5
|
||||
--> $DIR/future_not_send.rs:55:5
|
||||
|
|
||||
LL | let rt = &t;
|
||||
| -- has type `&T` which is not `Send`
|
||||
|
@ -114,7 +114,7 @@ LL | }
|
|||
= note: `T` doesn't implement `std::marker::Sync`
|
||||
|
||||
error: future cannot be sent between threads safely
|
||||
--> $DIR/future_not_send.rs:65:34
|
||||
--> $DIR/future_not_send.rs:66:34
|
||||
|
|
||||
LL | async fn unclear_future<T>(t: T) {}
|
||||
| ^
|
||||
|
|
67
tests/ui/manual_async_fn.fixed
Normal file
67
tests/ui/manual_async_fn.fixed
Normal file
|
@ -0,0 +1,67 @@
|
|||
// run-rustfix
|
||||
// edition:2018
|
||||
#![warn(clippy::manual_async_fn)]
|
||||
#![allow(unused)]
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
async fn fut() -> i32 { 42 }
|
||||
|
||||
async fn empty_fut() {}
|
||||
|
||||
async fn core_fut() -> i32 { 42 }
|
||||
|
||||
// should be ignored
|
||||
fn has_other_stmts() -> impl core::future::Future<Output = i32> {
|
||||
let _ = 42;
|
||||
async move { 42 }
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
fn not_fut() -> i32 {
|
||||
42
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
async fn already_async() -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
|
||||
struct S {}
|
||||
impl S {
|
||||
async fn inh_fut() -> i32 {
|
||||
// NOTE: this code is here just to check that the identation is correct in the suggested fix
|
||||
let a = 42;
|
||||
let b = 21;
|
||||
if a < b {
|
||||
let c = 21;
|
||||
let d = 42;
|
||||
if c < d {
|
||||
let _ = 42;
|
||||
}
|
||||
}
|
||||
42
|
||||
}
|
||||
|
||||
async fn meth_fut(&self) -> i32 { 42 }
|
||||
|
||||
async fn empty_fut(&self) {}
|
||||
|
||||
// should be ignored
|
||||
fn not_fut(&self) -> i32 {
|
||||
42
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
fn has_other_stmts() -> impl core::future::Future<Output = i32> {
|
||||
let _ = 42;
|
||||
async move { 42 }
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
async fn already_async(&self) -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
79
tests/ui/manual_async_fn.rs
Normal file
79
tests/ui/manual_async_fn.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
// run-rustfix
|
||||
// edition:2018
|
||||
#![warn(clippy::manual_async_fn)]
|
||||
#![allow(unused)]
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
fn fut() -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
|
||||
fn empty_fut() -> impl Future<Output = ()> {
|
||||
async {}
|
||||
}
|
||||
|
||||
fn core_fut() -> impl core::future::Future<Output = i32> {
|
||||
async move { 42 }
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
fn has_other_stmts() -> impl core::future::Future<Output = i32> {
|
||||
let _ = 42;
|
||||
async move { 42 }
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
fn not_fut() -> i32 {
|
||||
42
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
async fn already_async() -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
|
||||
struct S {}
|
||||
impl S {
|
||||
fn inh_fut() -> impl Future<Output = i32> {
|
||||
async {
|
||||
// NOTE: this code is here just to check that the identation is correct in the suggested fix
|
||||
let a = 42;
|
||||
let b = 21;
|
||||
if a < b {
|
||||
let c = 21;
|
||||
let d = 42;
|
||||
if c < d {
|
||||
let _ = 42;
|
||||
}
|
||||
}
|
||||
42
|
||||
}
|
||||
}
|
||||
|
||||
fn meth_fut(&self) -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
|
||||
fn empty_fut(&self) -> impl Future<Output = ()> {
|
||||
async {}
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
fn not_fut(&self) -> i32 {
|
||||
42
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
fn has_other_stmts() -> impl core::future::Future<Output = i32> {
|
||||
let _ = 42;
|
||||
async move { 42 }
|
||||
}
|
||||
|
||||
// should be ignored
|
||||
async fn already_async(&self) -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
98
tests/ui/manual_async_fn.stderr
Normal file
98
tests/ui/manual_async_fn.stderr
Normal file
|
@ -0,0 +1,98 @@
|
|||
error: this function can be simplified using the `async fn` syntax
|
||||
--> $DIR/manual_async_fn.rs:8:1
|
||||
|
|
||||
LL | fn fut() -> impl Future<Output = i32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::manual-async-fn` implied by `-D warnings`
|
||||
help: make the function `async` and return the output of the future directly
|
||||
|
|
||||
LL | async fn fut() -> i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
help: move the body of the async block to the enclosing function
|
||||
|
|
||||
LL | fn fut() -> impl Future<Output = i32> { 42 }
|
||||
| ^^^^^^
|
||||
|
||||
error: this function can be simplified using the `async fn` syntax
|
||||
--> $DIR/manual_async_fn.rs:12:1
|
||||
|
|
||||
LL | fn empty_fut() -> impl Future<Output = ()> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: make the function `async` and remove the return type
|
||||
|
|
||||
LL | async fn empty_fut() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
help: move the body of the async block to the enclosing function
|
||||
|
|
||||
LL | fn empty_fut() -> impl Future<Output = ()> {}
|
||||
| ^^
|
||||
|
||||
error: this function can be simplified using the `async fn` syntax
|
||||
--> $DIR/manual_async_fn.rs:16:1
|
||||
|
|
||||
LL | fn core_fut() -> impl core::future::Future<Output = i32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: make the function `async` and return the output of the future directly
|
||||
|
|
||||
LL | async fn core_fut() -> i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: move the body of the async block to the enclosing function
|
||||
|
|
||||
LL | fn core_fut() -> impl core::future::Future<Output = i32> { 42 }
|
||||
| ^^^^^^
|
||||
|
||||
error: this function can be simplified using the `async fn` syntax
|
||||
--> $DIR/manual_async_fn.rs:38:5
|
||||
|
|
||||
LL | fn inh_fut() -> impl Future<Output = i32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: make the function `async` and return the output of the future directly
|
||||
|
|
||||
LL | async fn inh_fut() -> i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: move the body of the async block to the enclosing function
|
||||
|
|
||||
LL | fn inh_fut() -> impl Future<Output = i32> {
|
||||
LL | // NOTE: this code is here just to check that the identation is correct in the suggested fix
|
||||
LL | let a = 42;
|
||||
LL | let b = 21;
|
||||
LL | if a < b {
|
||||
LL | let c = 21;
|
||||
...
|
||||
|
||||
error: this function can be simplified using the `async fn` syntax
|
||||
--> $DIR/manual_async_fn.rs:54:5
|
||||
|
|
||||
LL | fn meth_fut(&self) -> impl Future<Output = i32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: make the function `async` and return the output of the future directly
|
||||
|
|
||||
LL | async fn meth_fut(&self) -> i32 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: move the body of the async block to the enclosing function
|
||||
|
|
||||
LL | fn meth_fut(&self) -> impl Future<Output = i32> { 42 }
|
||||
| ^^^^^^
|
||||
|
||||
error: this function can be simplified using the `async fn` syntax
|
||||
--> $DIR/manual_async_fn.rs:58:5
|
||||
|
|
||||
LL | fn empty_fut(&self) -> impl Future<Output = ()> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: make the function `async` and remove the return type
|
||||
|
|
||||
LL | async fn empty_fut(&self) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: move the body of the async block to the enclosing function
|
||||
|
|
||||
LL | fn empty_fut(&self) -> impl Future<Output = ()> {}
|
||||
| ^^
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
|
@ -98,6 +98,21 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) {
|
|||
for i in from..from + 3 {
|
||||
dst[i] = src[i - from];
|
||||
}
|
||||
|
||||
#[allow(clippy::identity_op)]
|
||||
for i in 0..5 {
|
||||
dst[i - 0] = src[i];
|
||||
}
|
||||
|
||||
#[allow(clippy::reverse_range_loop)]
|
||||
for i in 0..0 {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
|
||||
// `RangeTo` `for` loop - don't trigger lint
|
||||
for i in 0.. {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
#[warn(clippy::needless_range_loop, clippy::manual_memcpy)]
|
||||
|
|
|
@ -58,19 +58,31 @@ error: it looks like you're manually copying between slices
|
|||
--> $DIR/manual_memcpy.rs:94:14
|
||||
|
|
||||
LL | for i in from..from + src.len() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[0..(from + src.len() - from)])`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[..(from + src.len() - from)])`
|
||||
|
||||
error: it looks like you're manually copying between slices
|
||||
--> $DIR/manual_memcpy.rs:98:14
|
||||
|
|
||||
LL | for i in from..from + 3 {
|
||||
| ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[0..(from + 3 - from)])`
|
||||
| ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[..(from + 3 - from)])`
|
||||
|
||||
error: it looks like you're manually copying between slices
|
||||
--> $DIR/manual_memcpy.rs:105:14
|
||||
--> $DIR/manual_memcpy.rs:103:14
|
||||
|
|
||||
LL | for i in 0..5 {
|
||||
| ^^^^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5])`
|
||||
|
||||
error: it looks like you're manually copying between slices
|
||||
--> $DIR/manual_memcpy.rs:108:14
|
||||
|
|
||||
LL | for i in 0..0 {
|
||||
| ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])`
|
||||
|
||||
error: it looks like you're manually copying between slices
|
||||
--> $DIR/manual_memcpy.rs:120:14
|
||||
|
|
||||
LL | for i in 0..src.len() {
|
||||
| ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])`
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
|
|
137
tests/ui/manual_non_exhaustive.rs
Normal file
137
tests/ui/manual_non_exhaustive.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
#![warn(clippy::manual_non_exhaustive)]
|
||||
#![allow(unused)]
|
||||
|
||||
mod enums {
|
||||
enum E {
|
||||
A,
|
||||
B,
|
||||
#[doc(hidden)]
|
||||
_C,
|
||||
}
|
||||
|
||||
// user forgot to remove the marker
|
||||
#[non_exhaustive]
|
||||
enum Ep {
|
||||
A,
|
||||
B,
|
||||
#[doc(hidden)]
|
||||
_C,
|
||||
}
|
||||
|
||||
// marker variant does not have doc hidden attribute, should be ignored
|
||||
enum NoDocHidden {
|
||||
A,
|
||||
B,
|
||||
_C,
|
||||
}
|
||||
|
||||
// name of variant with doc hidden does not start with underscore, should be ignored
|
||||
enum NoUnderscore {
|
||||
A,
|
||||
B,
|
||||
#[doc(hidden)]
|
||||
C,
|
||||
}
|
||||
|
||||
// variant with doc hidden is not unit, should be ignored
|
||||
enum NotUnit {
|
||||
A,
|
||||
B,
|
||||
#[doc(hidden)]
|
||||
_C(bool),
|
||||
}
|
||||
|
||||
// variant with doc hidden is the only one, should be ignored
|
||||
enum OnlyMarker {
|
||||
#[doc(hidden)]
|
||||
_A,
|
||||
}
|
||||
|
||||
// variant with multiple markers, should be ignored
|
||||
enum MultipleMarkers {
|
||||
A,
|
||||
#[doc(hidden)]
|
||||
_B,
|
||||
#[doc(hidden)]
|
||||
_C,
|
||||
}
|
||||
|
||||
// already non_exhaustive and no markers, should be ignored
|
||||
#[non_exhaustive]
|
||||
enum NonExhaustive {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
}
|
||||
|
||||
mod structs {
|
||||
struct S {
|
||||
pub a: i32,
|
||||
pub b: i32,
|
||||
_c: (),
|
||||
}
|
||||
|
||||
// user forgot to remove the private field
|
||||
#[non_exhaustive]
|
||||
struct Sp {
|
||||
pub a: i32,
|
||||
pub b: i32,
|
||||
_c: (),
|
||||
}
|
||||
|
||||
// some other fields are private, should be ignored
|
||||
struct PrivateFields {
|
||||
a: i32,
|
||||
pub b: i32,
|
||||
_c: (),
|
||||
}
|
||||
|
||||
// private field name does not start with underscore, should be ignored
|
||||
struct NoUnderscore {
|
||||
pub a: i32,
|
||||
pub b: i32,
|
||||
c: (),
|
||||
}
|
||||
|
||||
// private field is not unit type, should be ignored
|
||||
struct NotUnit {
|
||||
pub a: i32,
|
||||
pub b: i32,
|
||||
_c: i32,
|
||||
}
|
||||
|
||||
// private field is the only field, should be ignored
|
||||
struct OnlyMarker {
|
||||
_a: (),
|
||||
}
|
||||
|
||||
// already non exhaustive and no private fields, should be ignored
|
||||
#[non_exhaustive]
|
||||
struct NonExhaustive {
|
||||
pub a: i32,
|
||||
pub b: i32,
|
||||
}
|
||||
}
|
||||
|
||||
mod tuple_structs {
|
||||
struct T(pub i32, pub i32, ());
|
||||
|
||||
// user forgot to remove the private field
|
||||
#[non_exhaustive]
|
||||
struct Tp(pub i32, pub i32, ());
|
||||
|
||||
// some other fields are private, should be ignored
|
||||
struct PrivateFields(pub i32, i32, ());
|
||||
|
||||
// private field is not unit type, should be ignored
|
||||
struct NotUnit(pub i32, pub i32, i32);
|
||||
|
||||
// private field is the only field, should be ignored
|
||||
struct OnlyMarker(());
|
||||
|
||||
// already non exhaustive and no private fields, should be ignored
|
||||
#[non_exhaustive]
|
||||
struct NonExhaustive(pub i32, pub i32);
|
||||
}
|
||||
|
||||
fn main() {}
|
103
tests/ui/manual_non_exhaustive.stderr
Normal file
103
tests/ui/manual_non_exhaustive.stderr
Normal file
|
@ -0,0 +1,103 @@
|
|||
error: this seems like a manual implementation of the non-exhaustive pattern
|
||||
--> $DIR/manual_non_exhaustive.rs:5:5
|
||||
|
|
||||
LL | enum E {
|
||||
| ^-----
|
||||
| |
|
||||
| _____help: add the attribute: `#[non_exhaustive] enum E`
|
||||
| |
|
||||
LL | | A,
|
||||
LL | | B,
|
||||
LL | | #[doc(hidden)]
|
||||
LL | | _C,
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
|
||||
help: remove this variant
|
||||
--> $DIR/manual_non_exhaustive.rs:9:9
|
||||
|
|
||||
LL | _C,
|
||||
| ^^
|
||||
|
||||
error: this seems like a manual implementation of the non-exhaustive pattern
|
||||
--> $DIR/manual_non_exhaustive.rs:14:5
|
||||
|
|
||||
LL | / enum Ep {
|
||||
LL | | A,
|
||||
LL | | B,
|
||||
LL | | #[doc(hidden)]
|
||||
LL | | _C,
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
help: remove this variant
|
||||
--> $DIR/manual_non_exhaustive.rs:18:9
|
||||
|
|
||||
LL | _C,
|
||||
| ^^
|
||||
|
||||
error: this seems like a manual implementation of the non-exhaustive pattern
|
||||
--> $DIR/manual_non_exhaustive.rs:68:5
|
||||
|
|
||||
LL | struct S {
|
||||
| ^-------
|
||||
| |
|
||||
| _____help: add the attribute: `#[non_exhaustive] struct S`
|
||||
| |
|
||||
LL | | pub a: i32,
|
||||
LL | | pub b: i32,
|
||||
LL | | _c: (),
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
help: remove this field
|
||||
--> $DIR/manual_non_exhaustive.rs:71:9
|
||||
|
|
||||
LL | _c: (),
|
||||
| ^^^^^^
|
||||
|
||||
error: this seems like a manual implementation of the non-exhaustive pattern
|
||||
--> $DIR/manual_non_exhaustive.rs:76:5
|
||||
|
|
||||
LL | / struct Sp {
|
||||
LL | | pub a: i32,
|
||||
LL | | pub b: i32,
|
||||
LL | | _c: (),
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
help: remove this field
|
||||
--> $DIR/manual_non_exhaustive.rs:79:9
|
||||
|
|
||||
LL | _c: (),
|
||||
| ^^^^^^
|
||||
|
||||
error: this seems like a manual implementation of the non-exhaustive pattern
|
||||
--> $DIR/manual_non_exhaustive.rs:117:5
|
||||
|
|
||||
LL | struct T(pub i32, pub i32, ());
|
||||
| --------^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| help: add the attribute: `#[non_exhaustive] struct T`
|
||||
|
|
||||
help: remove this field
|
||||
--> $DIR/manual_non_exhaustive.rs:117:32
|
||||
|
|
||||
LL | struct T(pub i32, pub i32, ());
|
||||
| ^^
|
||||
|
||||
error: this seems like a manual implementation of the non-exhaustive pattern
|
||||
--> $DIR/manual_non_exhaustive.rs:121:5
|
||||
|
|
||||
LL | struct Tp(pub i32, pub i32, ());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove this field
|
||||
--> $DIR/manual_non_exhaustive.rs:121:33
|
||||
|
|
||||
LL | struct Tp(pub i32, pub i32, ());
|
||||
| ^^
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
|
@ -120,6 +120,27 @@ fn match_with_array() {
|
|||
}
|
||||
}
|
||||
|
||||
fn match_with_endless_range() {
|
||||
let arr = vec![0, 1, 2, 3];
|
||||
let range = ..;
|
||||
|
||||
// Ok
|
||||
match arr[range] {
|
||||
[0, 1] => println!("0 1"),
|
||||
[1, 2] => println!("1 2"),
|
||||
[0, 1, 2, 3] => println!("0, 1, 2, 3"),
|
||||
_ => {},
|
||||
}
|
||||
|
||||
// Ok
|
||||
match arr[..] {
|
||||
[0, 1] => println!("0 1"),
|
||||
[1, 2] => println!("1 2"),
|
||||
[0, 1, 2, 3] => println!("0, 1, 2, 3"),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
match_with_wildcard();
|
||||
match_without_wildcard();
|
||||
|
@ -127,4 +148,5 @@ fn main() {
|
|||
match_vec_ref();
|
||||
match_with_get();
|
||||
match_with_array();
|
||||
match_with_endless_range();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#![warn(clippy::while_let_on_iterator)]
|
||||
#![allow(clippy::never_loop, unreachable_code, unused_mut)]
|
||||
#![feature(or_patterns)]
|
||||
|
||||
fn base() {
|
||||
let mut iter = 1..20;
|
||||
|
@ -77,6 +78,62 @@ fn refutable() {
|
|||
// */
|
||||
}
|
||||
|
||||
fn refutable2() {
|
||||
// Issue 3780
|
||||
{
|
||||
let v = vec![1, 2, 3];
|
||||
let mut it = v.windows(2);
|
||||
while let Some([x, y]) = it.next() {
|
||||
println!("x: {}", x);
|
||||
println!("y: {}", y);
|
||||
}
|
||||
|
||||
let mut it = v.windows(2);
|
||||
while let Some([x, ..]) = it.next() {
|
||||
println!("x: {}", x);
|
||||
}
|
||||
|
||||
let mut it = v.windows(2);
|
||||
while let Some([.., y]) = it.next() {
|
||||
println!("y: {}", y);
|
||||
}
|
||||
|
||||
let mut it = v.windows(2);
|
||||
for [..] in it {}
|
||||
|
||||
let v = vec![[1], [2], [3]];
|
||||
let mut it = v.iter();
|
||||
while let Some([1]) = it.next() {}
|
||||
|
||||
let mut it = v.iter();
|
||||
for [_x] in it {}
|
||||
}
|
||||
|
||||
// binding
|
||||
{
|
||||
let v = vec![1, 2, 3];
|
||||
let mut it = v.iter();
|
||||
while let Some(x @ 1) = it.next() {
|
||||
println!("{}", x);
|
||||
}
|
||||
|
||||
let v = vec![[1], [2], [3]];
|
||||
let mut it = v.iter();
|
||||
for x @ [_] in it {
|
||||
println!("{:?}", x);
|
||||
}
|
||||
}
|
||||
|
||||
// false negative
|
||||
{
|
||||
let v = vec![1, 2, 3];
|
||||
let mut it = v.iter().map(Some);
|
||||
while let Some(Some(_) | None) = it.next() {
|
||||
println!("1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_loops() {
|
||||
let a = [42, 1337];
|
||||
let mut y = a.iter();
|
||||
|
@ -152,6 +209,7 @@ fn issue1654() {
|
|||
fn main() {
|
||||
base();
|
||||
refutable();
|
||||
refutable2();
|
||||
nested_loops();
|
||||
issue1121();
|
||||
issue2965();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#![warn(clippy::while_let_on_iterator)]
|
||||
#![allow(clippy::never_loop, unreachable_code, unused_mut)]
|
||||
#![feature(or_patterns)]
|
||||
|
||||
fn base() {
|
||||
let mut iter = 1..20;
|
||||
|
@ -77,6 +78,62 @@ fn refutable() {
|
|||
// */
|
||||
}
|
||||
|
||||
fn refutable2() {
|
||||
// Issue 3780
|
||||
{
|
||||
let v = vec![1, 2, 3];
|
||||
let mut it = v.windows(2);
|
||||
while let Some([x, y]) = it.next() {
|
||||
println!("x: {}", x);
|
||||
println!("y: {}", y);
|
||||
}
|
||||
|
||||
let mut it = v.windows(2);
|
||||
while let Some([x, ..]) = it.next() {
|
||||
println!("x: {}", x);
|
||||
}
|
||||
|
||||
let mut it = v.windows(2);
|
||||
while let Some([.., y]) = it.next() {
|
||||
println!("y: {}", y);
|
||||
}
|
||||
|
||||
let mut it = v.windows(2);
|
||||
while let Some([..]) = it.next() {}
|
||||
|
||||
let v = vec![[1], [2], [3]];
|
||||
let mut it = v.iter();
|
||||
while let Some([1]) = it.next() {}
|
||||
|
||||
let mut it = v.iter();
|
||||
while let Some([_x]) = it.next() {}
|
||||
}
|
||||
|
||||
// binding
|
||||
{
|
||||
let v = vec![1, 2, 3];
|
||||
let mut it = v.iter();
|
||||
while let Some(x @ 1) = it.next() {
|
||||
println!("{}", x);
|
||||
}
|
||||
|
||||
let v = vec![[1], [2], [3]];
|
||||
let mut it = v.iter();
|
||||
while let Some(x @ [_]) = it.next() {
|
||||
println!("{:?}", x);
|
||||
}
|
||||
}
|
||||
|
||||
// false negative
|
||||
{
|
||||
let v = vec![1, 2, 3];
|
||||
let mut it = v.iter().map(Some);
|
||||
while let Some(Some(_) | None) = it.next() {
|
||||
println!("1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_loops() {
|
||||
let a = [42, 1337];
|
||||
let mut y = a.iter();
|
||||
|
@ -152,6 +209,7 @@ fn issue1654() {
|
|||
fn main() {
|
||||
base();
|
||||
refutable();
|
||||
refutable2();
|
||||
nested_loops();
|
||||
issue1121();
|
||||
issue2965();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: this loop could be written as a `for` loop
|
||||
--> $DIR/while_let_on_iterator.rs:8:5
|
||||
--> $DIR/while_let_on_iterator.rs:9:5
|
||||
|
|
||||
LL | while let Option::Some(x) = iter.next() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
|
||||
|
@ -7,22 +7,40 @@ LL | while let Option::Some(x) = iter.next() {
|
|||
= note: `-D clippy::while-let-on-iterator` implied by `-D warnings`
|
||||
|
||||
error: this loop could be written as a `for` loop
|
||||
--> $DIR/while_let_on_iterator.rs:13:5
|
||||
--> $DIR/while_let_on_iterator.rs:14:5
|
||||
|
|
||||
LL | while let Some(x) = iter.next() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
|
||||
|
||||
error: this loop could be written as a `for` loop
|
||||
--> $DIR/while_let_on_iterator.rs:18:5
|
||||
--> $DIR/while_let_on_iterator.rs:19:5
|
||||
|
|
||||
LL | while let Some(_) = iter.next() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
|
||||
|
||||
error: this loop could be written as a `for` loop
|
||||
--> $DIR/while_let_on_iterator.rs:97:9
|
||||
--> $DIR/while_let_on_iterator.rs:102:9
|
||||
|
|
||||
LL | while let Some([..]) = it.next() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it`
|
||||
|
||||
error: this loop could be written as a `for` loop
|
||||
--> $DIR/while_let_on_iterator.rs:109:9
|
||||
|
|
||||
LL | while let Some([_x]) = it.next() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it`
|
||||
|
||||
error: this loop could be written as a `for` loop
|
||||
--> $DIR/while_let_on_iterator.rs:122:9
|
||||
|
|
||||
LL | while let Some(x @ [_]) = it.next() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it`
|
||||
|
||||
error: this loop could be written as a `for` loop
|
||||
--> $DIR/while_let_on_iterator.rs:154:9
|
||||
|
|
||||
LL | while let Some(_) = y.next() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
|
|
|
@ -155,3 +155,76 @@ fn test_weird_formatting() {
|
|||
exported();
|
||||
foo();
|
||||
}
|
||||
|
||||
mod super_imports {
|
||||
fn foofoo() {}
|
||||
|
||||
mod should_be_replaced {
|
||||
use super::foofoo;
|
||||
|
||||
fn with_super() {
|
||||
let _ = foofoo();
|
||||
}
|
||||
}
|
||||
|
||||
mod test_should_pass {
|
||||
use super::*;
|
||||
|
||||
fn with_super() {
|
||||
let _ = foofoo();
|
||||
}
|
||||
}
|
||||
|
||||
mod test_should_pass_inside_function {
|
||||
fn with_super_inside_function() {
|
||||
use super::*;
|
||||
let _ = foofoo();
|
||||
}
|
||||
}
|
||||
|
||||
mod test_should_pass_further_inside {
|
||||
fn insidefoo() {}
|
||||
mod inner {
|
||||
use super::*;
|
||||
fn with_super() {
|
||||
let _ = insidefoo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod should_be_replaced_futher_inside {
|
||||
fn insidefoo() {}
|
||||
mod inner {
|
||||
use super::insidefoo;
|
||||
fn with_super() {
|
||||
let _ = insidefoo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod use_explicit_should_be_replaced {
|
||||
use super_imports::foofoo;
|
||||
|
||||
fn with_explicit() {
|
||||
let _ = foofoo();
|
||||
}
|
||||
}
|
||||
|
||||
mod use_double_super_should_be_replaced {
|
||||
mod inner {
|
||||
use super::super::foofoo;
|
||||
|
||||
fn with_double_super() {
|
||||
let _ = foofoo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod use_super_explicit_should_be_replaced {
|
||||
use super::super::super_imports::foofoo;
|
||||
|
||||
fn with_super_explicit() {
|
||||
let _ = foofoo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,3 +156,76 @@ fn test_weird_formatting() {
|
|||
exported();
|
||||
foo();
|
||||
}
|
||||
|
||||
mod super_imports {
|
||||
fn foofoo() {}
|
||||
|
||||
mod should_be_replaced {
|
||||
use super::*;
|
||||
|
||||
fn with_super() {
|
||||
let _ = foofoo();
|
||||
}
|
||||
}
|
||||
|
||||
mod test_should_pass {
|
||||
use super::*;
|
||||
|
||||
fn with_super() {
|
||||
let _ = foofoo();
|
||||
}
|
||||
}
|
||||
|
||||
mod test_should_pass_inside_function {
|
||||
fn with_super_inside_function() {
|
||||
use super::*;
|
||||
let _ = foofoo();
|
||||
}
|
||||
}
|
||||
|
||||
mod test_should_pass_further_inside {
|
||||
fn insidefoo() {}
|
||||
mod inner {
|
||||
use super::*;
|
||||
fn with_super() {
|
||||
let _ = insidefoo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod should_be_replaced_futher_inside {
|
||||
fn insidefoo() {}
|
||||
mod inner {
|
||||
use super::*;
|
||||
fn with_super() {
|
||||
let _ = insidefoo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod use_explicit_should_be_replaced {
|
||||
use super_imports::*;
|
||||
|
||||
fn with_explicit() {
|
||||
let _ = foofoo();
|
||||
}
|
||||
}
|
||||
|
||||
mod use_double_super_should_be_replaced {
|
||||
mod inner {
|
||||
use super::super::*;
|
||||
|
||||
fn with_double_super() {
|
||||
let _ = foofoo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod use_super_explicit_should_be_replaced {
|
||||
use super::super::super_imports::*;
|
||||
|
||||
fn with_super_explicit() {
|
||||
let _ = foofoo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,5 +92,35 @@ LL | use crate:: fn_mod::
|
|||
LL | | *;
|
||||
| |_________^ help: try: `crate:: fn_mod::foo`
|
||||
|
||||
error: aborting due to 15 previous errors
|
||||
error: usage of wildcard import
|
||||
--> $DIR/wildcard_imports.rs:164:13
|
||||
|
|
||||
LL | use super::*;
|
||||
| ^^^^^^^^ help: try: `super::foofoo`
|
||||
|
||||
error: usage of wildcard import
|
||||
--> $DIR/wildcard_imports.rs:199:17
|
||||
|
|
||||
LL | use super::*;
|
||||
| ^^^^^^^^ help: try: `super::insidefoo`
|
||||
|
||||
error: usage of wildcard import
|
||||
--> $DIR/wildcard_imports.rs:207:13
|
||||
|
|
||||
LL | use super_imports::*;
|
||||
| ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo`
|
||||
|
||||
error: usage of wildcard import
|
||||
--> $DIR/wildcard_imports.rs:216:17
|
||||
|
|
||||
LL | use super::super::*;
|
||||
| ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
|
||||
|
||||
error: usage of wildcard import
|
||||
--> $DIR/wildcard_imports.rs:225:13
|
||||
|
|
||||
LL | use super::super::super_imports::*;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
|
||||
|
||||
error: aborting due to 20 previous errors
|
||||
|
||||
|
|
Loading…
Reference in a new issue